react组件通信与生命周期

Posted ZZZ --- jh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了react组件通信与生命周期相关的知识,希望对你有一定的参考价值。

state和setState

state 组件自身状态

setState(updater[,callback])

  • updater 更新数据的方法/对象
  • callback 更新成功后的回调函数
  • 异步: react通常会集齐一批需要更新的组件,然后一次性更新来保证渲染的性能
  • 浅合并: Object.assign()

React 在调用 setstate 后,react 会将传入的参数对象和组件当前的状态 合并,触发调和过程, 在调和过程中,react 会根据新的状态构建 react 元素树重新渲染整个 UI 界面,在得到元素树之后,react 会自动计算新老节点的差异,根据差异对界面进行最小化重新渲染

组件通信与数据流

在React.js中,数据是从上自下流动(传递)的,也就是一个父组件可以把它的state/props通过props传递给它的子组件,但是子组件不能修改props.

React.js是单向数据流,如果子组件需要修改父组件的状态(数据),是通过回调函数方式来完成的

  • 父级向子级通信

    把数据添加子组件的属性中,然后子组件中从props属性中,获取父级传递过来的数据

  • 子级向父级通信

    在父级中定义相关的数据操作方法(或其他回调函数),把该方法传递给子级,在子级中调用该方法父级传递消息

  • 好友列表案例:

    <!-- app.js -->
    import React, { Component } from 'react';
    import FriendList from "./FriendList";
    class App extends Component {
      render() {
        return (<div>
          <FriendList/>
        </div>)
      }
    }
    export default App;
    
    <!-- FriendList.js -->
    import React, { Component } from 'react';
    import './FriendList.css';
    import data from './data';
    import Dl from './dl'
    export default class FriendList extends Component { 
      state = {
        isOpen : "" //哪一个是展开的
      }
      // 传递给子组件, 点击哪个子组件,子组件就将自身的name传参过来
      changeOpen = (name)=>{
        this.setState({isOpen:name})
      }
    
      render() {
        let {isOpen} = this.state;
        return (
          <div className="friend-list">
            {
              Object.keys(data).map((item, index) => {
                return (
                  <Dl
                    key={index}
                    name={item}
                    value={data[item]}
                    isOpen = {isOpen}
                    changeOpen = {this.changeOpen}
                  />
                )
              })
            }
          </div>
        )
      }
    }
    
    <!-- dl.js -->
    import React,{ Component } from "react";
    
    export default class Dl extends Component{
    
      render(){
        // console.log(this.props);
        let {title,list} = this.props.value;
        // name 自身数据的属性名
        // isOpen  哪一项需要展开的
        let {name,isOpen,changeOpen} = this.props;
        return (
          <div className={"friend-group " + (name===isOpen ? 'expanded' : '')}>
            <dt onClick={()=>{
                changeOpen(name)
              }
            }>{title} </dt>
            {
              list.map((item,index)=>{
                return <dd key={index} >{item.name}</dd>
              })
            }
          </div>
        )
      }
    }
    

跨组件通信context-扩展

  • React.createContext(defaultValue);

    { Consumer, Provider } = createContext(defaultValue)

    <!-- context.js -->
    import React,{ createContext } from "react";
    
    let context = createContext();
    let { Consumer, Provider } = context;
    
    export default context
    export { Consumer, Provider }
    
    <!-- app.js -->
        import React, { Component } from 'react';
    import FriendList from "./FriendList";
    import { Provider } from "./context";
    
    class App extends Component {
      // state = {
      //   info:'别浪~'
      // }
    
      render() {
        return (
          <Provider value={{
            info:'猥琐发育'
          }}>
            <div>
              <FriendList />
            </div>
          </Provider>
        )
      }
    }
    
    export default App;
    
    <!-- dl.js -->
    import React,{ Component } from "react";
    import { Consumer } from "./context";
    
    export default class Dl extends Component{
    
     render(){
       // console.log(this.props);
       let {title,list} = this.props.value;
       // name 自身数据的属性名
       // isOpen  哪一项需要展开的
       let {name,isOpen,changeOpen} = this.props;
       return (
         <div className={"friend-group " + (name===isOpen ? 'expanded' : '')}>
           <dt onClick={()=>{
               changeOpen(name)
             }
           }>{title} </dt>
    
           <p>
             <Consumer>{value=>value.info}</Consumer>
           </p>
    
           {
             list.map((item,index)=>{
               return <dd key={index} >{item.name}</dd>
             })
           }
         </div>
       )
     }
    }
    
  • Context, Provoder在父组件调用Provider传递数据

    value要传递数据

  • 接收数据

    • class.contextType = Context;

    • static.contextType = Context;

      this.context;

    • Context.Consumer {value=>value.info} 注意在使用不熟练时,最好不要再项目中使用context, context一般给第三方库使用

组件的生命周期

16.3版本之前的

挂载阶段

  • constructor

    constructor() 中完成了react数据的初始化, 它接收两个参数:props和context, 当想在函数内部使用这两个参数时,需使用super()传入这两个参数.

    注意: 只要使用了constructor()就必须写super(),否则会导致this指向错误.

  • componentWillMount — 组件渲染之前调用

    一般用在服务器渲染时. 代表的过程是组件已经经历了constructor()初始化数据后, 但是还未渲染DOM时.

  • render

  • componentDidMount — 组件第一次渲染之后调用

    组件第一次渲染完成,此时dom节点已经生成,可以在这里调用ajax请求, 返回数据setState后组件会重新渲染.

更新阶段

父组件更新引起组件更新

  • componentWillReceiveProps(nextProps) — 在组件接收到一个新的props时调用

    在接受父组件改变后的props需要重新渲染组件时用到的比较多

    接收一个参数nextProps

    通过对比nextProps和this.props,将nextProps的state为当前组件的state,从而重新渲染组件

  • shouldComponentUpdate(nextProps, nextState) — 判断组件是否更新html

    主要用于性能优化(部分更新)

    唯一用于控制组件重新渲染的生命周期,由于在react中,setState以后,state发生变化,组件会进入重新渲染的流程,在这里return false可以阻止组件的更新

    因为react父组件的重新渲染会导致其所有子组件的重新渲染,这个时候其实我们是不需要所有子组件都跟着重新渲染的,因此需要在子组件的该生命周期中做判断

  • componentWillUpdate(nextProps, nextState) render — 组件即将更新html时调用

    shouldComponentUpdate返回true以后,组件进入重新渲染的流程,进入componentWillUpdate,这里同样可以拿到nextProps和nextState。

  • componentDidUpdate(prevProps, prevState) — 在组件完成更新后立即调用

    组件更新完毕后,react只会在第一次初始化成功会进入componentDidmount,之后每次重新渲染后都会进入这个生命周期,这里可以拿到prevProps和prevState,即更新前的props和state。

组件自身更新

  • shouldComponentUpdate
  • componentWillUpdate
  • render
  • componentDidUpdate

卸载阶段

  • componentWillUnmount — 在组件移除之前调用

    在此处完成组件的卸载和数据的销毁

    1 clear你在组件中所有的setTimeout和setInterval

    2 移除所有组件中的监听 removeEventListener

    3 有时候我们会碰到这个warning: Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the undefined component.

    原因:因为你在组件中的ajax请求返回setState,而你组件销毁的时候,请求还未完成,因此会报warning

    解决方法:

    componentDidMount() {
        this.isMount === true
        axios.post().then((res) => {
        this.isMount && this.setState({   // 增加条件ismount为true时
          aaa:res
        })
    })
    }
    componentWillUnmount() {
        this.isMount === false
    }
    

16.3版本之后的

挂载阶段

  • constructor
  • static getDerivedStateFromProps(props, state)
    • 注意this问题
  • render
  • componentDidMount

更新阶段

父组件更新引起组件更新

  • static getDerivedStateFromProps(props, state)

    代替componentWillReceiveProps()。
    老版本中的componentWillReceiveProps()方法判断前后两个 props 是否相同,如果不同再将新的 props 更新到相应的 state 上去。这样做一来会破坏 state 数据的单一数据源,导致组件状态变得不可预测,另一方面也会增加组件的重绘次数。

    这两者最大的不同就是:
    在 componentWillReceiveProps 中,我们一般会做以下两件事,一是根据 props 来更新 state,二是触发一些回调,如动画或页面跳转等。

    1. 在老版本的 React 中,这两件事我们都需要在 componentWillReceiveProps 中去做。
    2. 而在新版本中,官方将更新 state 与触发回调重新分配到了 getDerivedStateFromProps 与 componentDidUpdate 中,使得组件整体的更新逻辑更为清晰。而且在 getDerivedStateFromProps 中还禁止了组件去访问 this.props,强制让开发者去比较 nextProps 与 prevState 中的值,以确保当开发者用到 getDerivedStateFromProps 这个生命周期函数时,就是在根据当前的 props 来更新组件的 state,而不是去做其他一些让组件自身状态变得更加不可预测的事情。
  • shouldComponentUpdate()

  • componentWillUpdate()

  • render()

  • getSnapshotBeforeUpdate(prevProps, prevState)

    代替componentWillUpdate。
    常见的 componentWillUpdate 的用例是在组件更新前,读取当前某个 DOM 元素的状态,并在 componentDidUpdate 中进行相应的处理。
    这两者的区别在于:

    1. 在 React 开启异步渲染模式后,在 render 阶段读取到的 DOM 元素状态并不总是和 commit 阶段相同,这就导致在
      componentDidUpdate 中使用 componentWillUpdate 中读取到的 DOM 元素状态是不安全的,因为这时的值很有可能已经失效了。
    2. getSnapshotBeforeUpdate 会在最终的 render 之前被调用,也就是说在 getSnapshotBeforeUpdate 中读取到的 DOM 元素状态是可以保证与 componentDidUpdate 中一致的。
      此生命周期返回的任何值都将作为参数传递给componentDidUpdate()
  • componentDidUpdate()

组件自身更新

  • shouldComponentUpdate()
  • componentWillUpdate()
  • render()
  • getSnapshotBeforeUpdate()
  • componentDidUpdate()

卸载阶段

  • componentWillUnmount

错误处理

解释: 当渲染过程,生命周期,或子组件的构造函数中抛出错误时,会调用以下的方法

  • static getDerivedStateFromError()
  • componentDidCatch(error, info) 参考:http://projects.wojtekmaj.pl/react-lifecycle-methodsdiagram/

受控组件

  • 需要同步value值(defaultValue, defaultChecked)
  • 类似于vue中的双向数据绑定, 数据和视图之间可以相互影响

非受控组件

  • 不需要同步value值(defaultValue, defaultChecked)
  • 类似于单向数据流, 只可以数据改变视图

总结

组件通信的三种方式和生命周期都是react中的重要知识点,详细见解也可以去官网查看

以上是关于react组件通信与生命周期的主要内容,如果未能解决你的问题,请参考以下文章

react讲解(组件,生命周期以及受控组件)

react讲解(组件,生命周期以及受控组件)

react组件通信与生命周期

react组件通信与生命周期

react组件通信与生命周期

react组件通信与生命周期