React 生命周期精讲

React生命周期主要包括三个阶段:创建阶段(Mounting)更新阶段(Updating)销毁阶段(Unmounting),在React不同的生命周期里,会依次触发不同的钩子函数。

一、创建阶段

1、constructor

组件被实例化的时候触发

该方法只会执行一次,调用该方法会返回一个组件实例。在初始化阶段执行,一般用做对组件做初始工作,如设置state等,可直接对 this.state 赋值。

constructor() {
  super();
  this.state = {number: 0}
}

通常,React构造函数仅用于以下两种情况:

  • 来初始化函数内部 state
  • 为事件处理函数绑定实例

如果不初始化 state 或不进行方法绑定,则不需要写 constructor() , 只需要设置 this.state 即可

不能在 constructor()构造函数内部调用 this.setState(), 因为此时第一次 render()还未执行,也就意味DOM节点还未挂载

2、static getDerivedStateFromProps(nextProps, state)

它是一个静态方法,接收 props state 两个参数。

getDerivedStateFromProps() 在调用 render方法之前调用,在初始化和后续更新都会被调用

返回值:返回一个对象来更新 state, 如果返回 null 则不更新任何内容

参数: 第一个参数为即将更新的 props, 第二个参数为上一个状态的 state , 可以比较props 和 state来加一些限制条件,防止无用的state更新

注意:getDerivedStateFromProps 是一个静态函数,不能使用this, 也就是只能作一些无副作用的操作

3、render()

render 方法是类组件中唯一必须实现的方法,它的返回值将作为页面渲染的视图。

注意: 不要在 render 里面 setState, 否则会触发死循环导致内存崩溃

4、componentDidMount()

该生命周期方法会在组件挂载之后执行,也只会执行一次。componentDidMount() 在组件挂载后 (插入DOM树后) 立即调用,componentDidMount() 是发送网络请求、启用事件监听方法的好时机,并且可以在 此钩子函数里直接调用 setState()。

二、更新阶段

1、static getDerivedStateFromProps() 同上

2、shouldComponentUpdate(nextProps, nextState)

shouldComponentUpdate() 在组件更新之前调用,可以控制组件是否进行更新, 返回true时组件更新, 返回false则不更新。但是首次渲染或者使用 forceUpdate 函数时不会被调用。

包含两个参数,第一个是即将更新的 props 值,第二个是即将跟新后的 state 值,可以根据更新前后的 props 或 state 来比较加一些限制条件,决定是否更新,进行性能优化。

注意:

不建议在 shouldComponentUpdate() 中进行深层比较或使用 JSON.stringify()。这样非常影响效率,且会损害性能

不要 shouldComponentUpdate 中调用 setState(),否则会导致无限循环调用更新、渲染,直至浏览器内存崩溃

可以使用内置 PureComponent 组件替代

3、render() 同上

4、getSnapshotBeforeUpdate(prevProps, prevState)

getSnapshotBeforeUpdate() 在最近一次的渲染输出被提交之前调用。也就是说,在 render 之后,即将对组件进行挂载时调用。此生命周期函数在最近一次渲染提交至 DOM 树之前执行,此时 DOM 树还未改变,我们可以在这里获取 DOM 改变前的信息,例如更新前 DOM 的滚动位置。

它可以使组件在 DOM 真正更新之前捕获一些信息(例如滚动位置),此生命周期返回的任何值都会作为参数传递给 componentDidUpdate()。如不需要传递任何值,那么请返回 null。

5、componentDidUpdate(prevProps, prevState, snapshot)

componentDidUpdate() 会在更新后会被立即调用首次渲染不会执行

包含三个参数,第一个是上一次props值。 第二个是上一次state值。如果组件实现了getSnapshotBeforeUpdate() 生命周期(不常用),第三个是“snapshot” 参数传递

可以进行前后props的比较进行条件语句的限制,来进行 setState() , 否则会导致死循环

三、卸载阶段

componentWillUnmount()

这个生命周期函数会在组件卸载以及销毁之前调用。

使用场景:

通常用来执行组件的清理操作,例如:清除 timer、取消网络请求、清除订阅等。

旧生命周期废弃原因

在v15的版本,更新过程是同步的,往往一个主线程长时间被占用,会导致页面性能问题,而React Fiber的机制: 利用浏览器 requestIdleCallback 将可中断的任务进行分片处理,每一个小片的运行时间很短,这样唯一的线程就不会被独占。但是分片之后会出现一系列问题,因为React Fiber Reconciliation 这个过程有可能暂停然后继续执行,所以挂载和更新之前的生命周期钩子就有可能不执行或者多次执行。

目前React为这几个生命周期钩子提供了别名,分别是:

  • UNSAFE_componentWillMount
  • UNSAFE_componentWillReceiveProps
  • UNSAFE_componentWillUpdate

实例:

父组件:Parent.js

import React, { Component } from 'react';
import { Button } from 'antd';
import Child from './child';

const parentStyle = {
  padding: 40,
  margin: 20,
  backgroundColor: 'LightCyan',
};

const NAME = 'Parent 组件:';

export default class Parent extends Component {
  constructor() {
    super();
    console.log(NAME, 'constructor');
    this.state = {
      count: 0,
      mountChild: true,
    };
  }

  static getDerivedStateFromProps(nextProps, prevState) {
    console.log(NAME, 'getDerivedStateFromProps');
    return null;
  }

  componentDidMount() {
    console.log(NAME, 'componentDidMount');
  }

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

  getSnapshotBeforeUpdate(prevProps, prevState) {
    console.log(NAME, 'getSnapshotBeforeUpdate');
    return null;
  }

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

  componentWillUnmount() {
    console.log(NAME, 'componentWillUnmount');
  }

  /**
   * 修改传给子组件属性 count 的方法
   */
  changeNum = () => {
    let { count } = this.state;
    this.setState({
      count: ++count,
    });
  };

  /**
   * 切换子组件挂载和卸载的方法
   */
  toggleMountChild = () => {
    const { mountChild } = this.state;
    this.setState({
      mountChild: !mountChild,
    });
  };

  render() {
    console.log(NAME, 'render');
    const { count, mountChild } = this.state;
    return (
      <div style={parentStyle}>
        <div>
          <h3>父组件</h3>
          <Button onClick={this.changeNum}>改变传给子组件的属性 count</Button>
          <br />
          <br />
          <Button onClick={this.toggleMountChild}>卸载 / 挂载子组件</Button>
        </div>
        {mountChild ? <Child count={count} /> : null}
      </div>
    );
  }
}

子组件: Child.js

import React, { Component } from 'react';
import { Button } from 'antd';

const childStyle = {
  padding: 20,
  margin: 20,
  backgroundColor: 'LightSkyBlue',
};

const NAME = 'Child 组件:';

export default class Child extends Component {
  constructor() {
    super();
    console.log(NAME, 'constructor');
    this.state = {
      counter: 0,
    };
  }

  static getDerivedStateFromProps(nextProps, prevState) {
    console.log(NAME, 'getDerivedStateFromProps');
    return null;
  }

  componentDidMount() {
    console.log(NAME, 'componentDidMount');
  }

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

  getSnapshotBeforeUpdate(prevProps, prevState) {
    console.log(NAME, 'getSnapshotBeforeUpdate');
    return null;
  }

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

  componentWillUnmount() {
    console.log(NAME, 'componentWillUnmount');
  }

  changeCounter = () => {
    let { counter } = this.state;
    this.setState({
      counter: ++counter,
    });
  };

  render() {
    console.log(NAME, 'render');
    const { count } = this.props;
    const { counter } = this.state;
    return (
      <div style={childStyle}>
        <h3>子组件</h3>
        <p>父组件传过来的属性 count : {count}</p>
        <p>子组件自身状态 counter : {counter}</p>
        <Button onClick={this.changeCounter}>改变自身状态 counter</Button>
      </div>
    );
  }
}

输出结果:

1、父子组件第一次进行渲染加载时:

控制台的打印顺序为:

  • Parent 组件: constructor()
  • Parent 组件: getDerivedStateFromProps()
  • Parent 组件: render()
  • Child 组件: constructor()
  • Child 组件: getDerivedStateFromProps()
  • Child 组件: render()
  • Child 组件: componentDidMount()
  • Parent 组件: componentDidMount()

2、子组件修改自身状态 state

点击子组件 [改变自身状态counter] 按钮,其 [自身状态counter] 值会 +1, 此时控制台的打印顺序为:

  • Child 组件: getDerivedStateFromProps()
  • Child 组件: shouldComponentUpdate()
  • Child 组件: render()
  • Child 组件: getSnapshotBeforeUpdate()
  • Child 组件: componentDidUpdate()

3、修改父组件中传入子组件的 props

点击父组件中的 [改变传给子组件的属性 count] 按钮,则界面上 [父组件传过来的属性 count] 的值会 + 1,控制台的打印顺序为:

  • Parent 组件: getDerivedStateFromProps()
  • Parent 组件: shouldComponentUpdate()
  • Parent 组件: render()
  • Child 组件: getDerivedStateFromProps()
  • Child 组件: shouldComponentUpdate()
  • Child 组件: render()
  • Child 组件: getSnapshotBeforeUpdate()
  • Parent 组件: getSnapshotBeforeUpdate()
  • Child 组件: componentDidUpdate()
  • Parent 组件: componentDidUpdate()

参考文献:


 

猜你喜欢

转载自blog.csdn.net/TongJ_/article/details/129959818