react入门系列之生命周期钩子函数

什么是生命周期函数

  1. 组件中在某个阶段会自动执行的函数。
    • 比如我们执行使用render函数,在prop或者state变化时,render函数自动执行。
    • 因此render函数就是一个生命周期函数。
  2. constructor在组件创建的时候也会自动调用。但是他不是react独有,是es6中的函数所以,我们不将他列为生命周期函数。

生命周期分为4个阶段

  1. initialization(组件初始化)
    • 我们在创建组件的时候需要继承react Component这个基类,也就继承这个react的基类,才能有render(),生命周期等方法可以使用,这也说明为什么函数组件不能使用这些方法的原因。同时也让组件能使用setState方法。
    • 然后我们在 constructor 构造函数中使用 super(props)来将父组件传递的props注入给这个组件,以及使用this.state初始化这个组件的属性。
    • 这一系列动作就是组件的初始化。
  2. mount (组件的挂载)
    • 此阶段分为三个时期
      1. componentWillMount(挂载之前)

        • 这个函数在组件挂载到DOM之前的时候执行,所以你在这里引用setState方法,是不会引起组件的重新渲染。
        • 同样这里做的事情如果放在constructor构造函数中去使用也是可以的。
        • 这个函数只会被调用一次,就是组件挂载到DOM之前的时候。其他时候是不会触发这个函数的。
      2. render(挂载中)

        • 根据组件的props和state是否变化,变化即执行,这里的变化要注意,并不是值变化。只要重新赋值,新旧值相同也会执行。
        • 然后return 一个React元素(描述组件,即UI),不负责组件实际渲染工作,之后由React自身根据此元素去渲染出页面DOM。render是纯函数(Pure function:函数的返回结果只依赖于它的参数;函数执行过程里面没有副作用。
        • 不能在里面执行this.setState,会有改变组件状态的副作用。
      3. componentDidMount(挂载完成)

        • 约定将ajax请求放在这个生命周期函数中
        • 组件挂载到DOM后被执行,只会执行一次。
  3. update (组件更新时)
    • 首先我们来了解一下什么时组件更新。只有在通过setState函数使sate和props变化或重新赋值时才叫组件更新。

    • setState引起父组件的render函数执行,同时也会引起它的子组件的render函数执行。

    • 原因是react虚拟DOM的diff算法,同级比较原理。

    • 只要重新赋值就是组件更新,如果值并没有变,也更新组件,这样就会耗性能,也是我们讲同级比较的时候说的一个弊端。

    • 接下来我们了解一下有那些周期函数

      1. componentWillReceiveProps(nextProps)

        • 此方法只调用于props引起的组件更新过程中,参数nextProps是父组件传给当前组件的新props。但父组件render方法的调用不能保证重传给当前组件的props是有变化的,所以在此方法中根据nextProps和this.props来查明重传的props是否改变,以及如果改变了要执行啥,比如根据新的props调用this.setState出发当前组件的重新render
      2. shouldComponentUpdate(nextProps, nextState)

        • 此方法通过比较nextProps,nextState及当前组件的this.props,this.state,返回true时当前组件将继续执行更新过程,返回false则当前组件更新停止,以此可用来减少组件的不必要渲染,优化组件性能。ps:这边也可以看出,就算componentWillReceiveProps()中执行了this.setState,更新了state,但在render前(如shouldComponentUpdate,componentWillUpdate),this.state依然指向更新前的state,不然nextState及当前组件的this.state的对比就一直是true了。
      3. componentWillUpdate(nextProps, nextState)

        • 此方法在调用render方法前执行,在这边可执行一些组件更新发生前的工作,一般较少用。
      4. render

        • render方法在上文讲过,这边只是重新调用。
      5. componentDidUpdate(prevProps, prevState)

        • 此方法在组件更新后被调用,可以操作组件更新的DOM,prevProps和prevState这两个参数指的是组件更新前的props和state
  • 优化弊端
  • 1.当因为父组件重新render,使得props重新被赋值,导致子组件跟着渲染.
/**
 * 方法一:解决上述弊端,可以在shouldComponentUpdate函数,组件更新前进行判断,props是否改变,再确定是否执行重新渲染。
*/
class Child extends Component {
   shouldComponentUpdate(nextProps){ 
        if(nextProps.value === this.props.value){
          return false
        }
        return true
    }
    render() {
        return <div>{this.props.value}</div>
    }
}
/**
 * 方法二:
 * 1.解决上述弊端,也可以先将this.props.value赋值给子组件的state
 * 2.再在componentWillReceiveProps函数中,使用setState去重新给stae中的属性赋值this.props.value
 * 3.文档中提到,在该函数(componentWillReceiveProps)中调用 this.setState() 将不会引起第二次渲染。
 * 4.因为componentWillReceiveProps是在props有变化的时候才会触发,所以在这里面做this.setState()一定是有改变state
*/
//
class Child extends Component {
    constructor(props) {
        super(props);
        this.state = {
            value: props.value // 先将this.props.value赋值给子组件的state
        };
    }
    componentWillReceiveProps(nextProps) { // 在props有变化的时候才会触发这个方法
        this.setState({value: nextProps.value}); // 重新赋值,引起render
    }
    render() {
        return <div>{this.state.value}</div>
    }
}
  • 2.组件本身调用setState方法,但是并没有改变state中的值。
/**
 * 也是通过shouldComponentUpdate判断新旧值是否改变,改变才做render
*/
class Test extends Component {
   constructor(props) {
        super(props);
        this.state = {
          value:1
        }
   }
   shouldComponentUpdate(nextState){ // 应该使用这个方法判断新旧值是否改变
        if(nextState.value === this.state.value){
          return false // 没有改变返回false
        }
        return true
    }

   changeState = () => { // 虽然调用了setState ,但state并无变化
        const value = this.state.value
         this.setState({
            value
         })
   }

    render() {
        return <div onClick = {this.changeState}>{this.state.value}</div>
    }
}

  1. 卸载阶段(componentWillUnmount)
  • 只有一个生命周期方法componentWillUnmount
    • 此方法在组件被卸载前调用,可以在这里执行一些清理工作,比如清楚组件中使用的定时器,清楚componentDidMount中手动创建的DOM元素等,以避免引起内存泄漏。
  1. 在react组件中,除了render函数,其他任何生命周期函数都可以不写,因为组件继承了react中的 Component,Component内置了其他的生命周期函数