深入react技术栈--React生命周期

956 阅读3分钟

导语

React组件的生命周期根据广义定义描述,可以分为挂载, 渲染和 卸载这几个阶段。 当渲染后的组件需要更新时, 我们会重新去渲染组件,直至卸载。
因此,我们可以把React生命周期分成两类:

  • 当组件在挂载或卸载时
  • 当组件接收新的数据时,即组件更新时

挂载或卸载过程

1.组件的挂载

组件挂载是最基本的过程,这是过程主要做的组件状态的初始化。比如

import React, { Component } from 'react';

export default class App extends Component {
    static propTypes = {
    }

    static defaultProps = {
    };

    constructor(props) {
        super(props);
        this.state = {
        };
    }

    // 这个在 React 16.9 就把componentWillMount 废弃了
    componentWillMount() {
        console.log('WillMount');
    }

    componentDidMount() {
        console.log('DidMount');
    }

    render() {
        return (
            <div>This is a demo.</div>
        )
    }
}

我们看到 propTypes 和 defaultProps 分别代表 props 类型检查和默认类型。 这两个属性被声明成静态属性, 意味着可以从类外面访问他们, 比如: App.propTypes 和 App.defaultProps

两个生命周期

  • componentWillMount : render渲染方法之前执行
  • componentDidMount : 在render渲染方法之后执行

NOTE

  1. 如果我们在 componentWillMount 中执行 setState方法, 会发生什么呢? 组件会更新 state, 但是组件只渲染一次。因此,并无意义,初始化时的 state都放在 this.state

  2. 如果我们在 componentDidMount 中执行setState方法, 又会发生什么呢?组件当然会再次更新, 不过在初始化过程就渲染了两次组件,并不好。但是有时候,比如计算组件的位置或宽度高度,就不得不让组件先渲染,更新必要的信息后,再次渲染。

组件的卸载

卸载只有一个:

  • componentWillUnmount : 卸载之前的生命周期 通常,在componentWillUnmount方法中, 我们会执行一些清理方法, 如事件回收,或清除定时器等。

数据更新过程

更新过程一般分为2种情况:

  • 父组件向下传递 props 发生更新
  • 组件自身执行 setState 发生更新
import React, { Component } from 'react';

export default class App extends Component {

    constructor(props) {
        super(props);

        this.state = {
            count: 0,
        }

        this.handleClick = this.handleClick.bind(this);
    }
    
    handleClick() {
        this.setState({
            count: this.state.count + 1,
        });
    }

    shouldComponentUpdate(nextProps, nextState) {
        console.log(nextProps, nextState);
        return true
    }

    componentWillUpdate(nextProps, nextState) {
        console.log(nextProps, nextState);
    }

    componentDidUpdate(prevProps, prevState) {
        console.log(prevProps, prevState);
    }

    render() {
        return (
            <div className="header">
                <button onClick={this.handleClick}>点击</button>
                <div>{this.state.count}</div>
            </div>
        )
    }
}

如果组件自身的 state更新了, 那么会依次执行 shouldComponentUpdate, componentWillUpdate , render 和 componentDidUpdate

  • shouldComponentUpdate 是一个特别的方法, 它接受需要更新的 props 和 state, 让开发者增肌必要的条件判断, 让其在需要时更新, 不需要时不更新。 因此, 当方法返回 false 的时候, 组件不再向下执行生命周期方法

shouldComponentUpdate 的本质是用来进行正确的组件渲染怎么理解呢?
比如: 当父节点 props 改变的时候, 在理想情况下, 只需渲染在一条链路上有关props改变的节点即可。但是,在默认情况下, React 会渲染所有的节点, 因为 shouldComponentUpdate 默认返回的 true.
正确的组件渲染从另一个意义上说,也是性能优化的方法之一

值得注意的是, 无状态组件是没有生命周期方法的, 这也意味着它没有 shouldComponentUpdate。 渲染到该类组件时,每次都会重新渲染。 为了更好的使用无状态组件, 我们可以选择引用 Recompose 库的 pure方法

  • componentWillUpdate 更新过程中渲染前

  • componentDidUpdate 更新过程渲染后

如果组件是由父组件更新 props 而更新的, 那么在 shouldComponentUpdate 之前会执行 componentWillReceiveProps 方法。

React新增的生命周期

getSnapshotBeforeUpdate(prevProps, prevState)

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

这两者的区别在于:

  1. 在react开启异步渲染模式后, componentDidUpdate 中使用 componentWillUpdate 中读取到的 DOM 元素状态是不安全的,因为这时的值很有可能已经失效了。

  2. getSnapshotBeforeUpdate 会在最终的 render 之前被调用,也就是说在 getSnapshotBeforeUpdate 中读取到的 DOM 元素状态是可以保证与 componentDidUpdate 中一致的。 此生命周期返回的任何值都将作为参数传递给componentDidUpdate()。

getDerivedStateFromProps(nextProps, prevState)

代替componentWillReceiveProps()。