React 组件生命周期详解

本文详细介绍了 React 生命周期的用法以及各个阶段的生命周期进行,而且用实例代码作了详细演示。代码位置html

话很少说,先上图

React生命周期图解

上图是基于 React 16.4 以后的生命周期图解。如感受不对,请先查看 React 版本react


React 生命周期详解

各个阶段的生命周期函数

constructor 构造函数

在 React 组件挂载以前被调用,实现 React.Component 的子类的构造函数时,要在第一行加上 super(props)。git

React 构造函数一般只用于两个目的:github

  • 经过分配一个对象到 this.state 来初始化本地 state
  • 将 事件处理程序 方法绑定到实例

若是没有初始化状态(state),而且没有绑定方法,一般不须要为 React 组件实现一个构造函数。浏览器

不须要在构造函数中调用 setState(),只需将初始状态设置给 this.state 便可 。性能优化

static getDerivedStateFromProps(nextProps, prevState)

getDerivedStateFromProps 在每次调用 render 方法以前调用。包括初始化和后续更新时。网络

包含两个参数:第一个参数为即将更新的 props 值,第二个参数为以前的 state函数

返回值:返回为 null 时,不作任何反作用处理。假若想更新某些 state 状态值,则返回一个对象,就会对 state 进行修改性能

该生命周期是静态函数,属于类的方法,其做用域内是找不到 this测试

render()

render() 方法是类组件中惟一必须的方法,其他生命周期不是必需要写。 组件渲染时会走到该生命周期,展现的组件都是由 render() 生命周期的返回值来决定。

注意: 若是 shouldComponentUpdate() 方法返回 false ,render() 不会被调用。

componentDidMount()

在 React 组件装载(mounting)(插入树)后被当即调用。

componentDidMount 生命周期是进行发送网络请求、启用事件监听的好时机

若是有必要,能够在今生命周期中马上调用 setState()

shouldComponentUpdate(nextProps, nextState)

在组件准备更新以前调用,能够控制组件是否进行更新,返回 true 时组件更新,返回 false 组件不更新。

包含两个参数,第一个是即将更新的 props 值,第二个是即将跟新后的 state 值,能够根据更新先后的 props 或 state 进行判断,决定是否更新,进行性能优化

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

getSnapshotBeforeUpdate(prevProps, prevState)

getSnapshotBeforeUpdate() 在最近一次的渲染输出被提交以前调用。也就是说,在 render 以后,即将对组件进行挂载时调用。

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

componentDidUpdate(prevProps, prevState, snapshot)

componentDidUpdate() 在更新发生以后当即被调用。这个生命周期在组件第一次渲染时不会触发。

能够在今生命周期中调用 setState(),可是必须包含在条件语句中,不然会形成无限循环,最终致使浏览器内存崩溃

componentWillUnmount()

componentWillUnmount() 在组件即将被卸载或销毁时进行调用。

今生命周期是取消网络请求、移除监听事件清理 DOM 元素清理定时器等操做的好时机

注意: componentWillMount()、componentWillUpdate()、componentWillReceiveProps() 即将被废弃,请不要再在组件中进行使用。所以本文不作讲解,避免混淆。

生命周期执行顺序

挂载时

  • constructor()

  • static getDerivedStateFromProps()

  • render()

  • componentDidMount()

更新时

  • static getDerivedStateFromProps()

  • shouldComponentUpdate()

  • render()

  • getSnapshotBeforeUpdate()

  • componentDidUpdate()

卸载时

  • componentWillUnmount()

生命周期中是否可调用 setState()

初始化 state

  • constructor()

能够调用 setState()

  • componentDidMount()

根据判断条件能够调用 setState()

  • componentDidUpdate()

禁止调用 setState()

  • shouldComponentUpdate()

  • render()

  • getSnapshotBeforeUpdate()

  • componentWillUnmount()


实例演示

源码地址

下面根据一个父子组件的props 改变、state 改变以及子组件的挂载/卸载等事件,对各生命周期执行顺序进行理解,有兴趣的同窗能够一块儿看一下,也能够下载代码本身进行测试。

编写组件代码

父组件:Parent.js

import React, { Component } from 'react';

import Child from './Child.js';

const parentStyle = {
    padding: 40,
    margin: 20,
    border: '1px solid pink'
}

const TAG = "Parent 组件:"

export default class Parent extends Component {

    constructor(props) {
        super(props);
        console.log(TAG, 'constructor');
        this.state = {
            num: 0,
            mountChild: true
        }
    }

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

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

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

    getSnapshotBeforeUpdate(prevProps, prevState) {
        console.log(TAG, 'getSnapshotBeforeUpdate');
        return null;
    }
    
    componentDidUpdate(prevProps, prevState, snapshot) {
        console.log(TAG, 'componentDidUpdate');
    }

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


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

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

    render() {
        console.log(TAG, 'render');
        const { num, mountChild } = this.state;
        return (
            <div style={ parentStyle }> <div> <p>父组件</p> <button onClick={ this.changeNum }>改变传给子组件的属性 num</button> <br /> <br /> <button onClick={ this.toggleMountChild }>卸载 / 挂载子组件</button> </div> { mountChild ? <Child num={ num } /> : null } </div> ) } } 复制代码

子组件:Child.js

import React, { Component } from 'react'


const childStyle = {
    padding: 20,
    margin: 20,
    border: '1px solid black'
}

const TAG = 'Child 组件:'

export default class Child extends Component {

    constructor(props) {
        super(props);
        console.log(TAG, 'constructor');
        this.state = {
            counter: 0
        };
    }

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

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

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

    getSnapshotBeforeUpdate(prevProps, prevState) {
        console.log(TAG, 'getSnapshotBeforeUpdate');
        return null;
    }
    
    componentDidUpdate(prevProps, prevState, snapshot) {
        console.log(TAG, 'componentDidUpdate');
    }

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

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

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

从五种组件状态改变的时机来探究生命周期的执行顺序

1、父子组件初始化

父子组件第一次进行渲染加载时,界面展现为:

初始化展现界面

控制台中的 log 打印顺序为:

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

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

点击子组件中的 改变自身状态 按钮,则界面上 自身状态 counter: 的值会 + 1,控制台中的 log 打印顺序为:

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

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

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

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

4、卸载子组件

点击父组件中的 卸载 / 挂载子组件 按钮,则界面上子组件会消失,控制台中的 log 打印顺序为:

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

5、从新挂载子组件

再次点击父组件中的 卸载 / 挂载子组件 按钮,则界面上子组件会从新渲染出来,控制台中的 log 打印顺序为:

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

父子组件生命周期执行顺序总结:

  • 当子组件自身状态改变时,不会对父组件产生反作用的状况下,父组件不会进行更新,即不会触发父组件的生命周期

  • 当父组件中状态发生变化(包括子组件的挂载以及)时,会触发自身对应的生命周期以及子组件的更新

    • render 以及 render 以前的生命周期,则 父组件 先执行

    • render 以及 render 以后的声明周期,则子组件先执行,而且是与父组件交替执行

  • 当子组件进行卸载时,只会执行自身的 componentWillUnmount 生命周期,不会再触发别的生命周期

可能总结的很差,不是很完整。只是根据通常状况进行的总结。有不妥之处,但愿各位朋友可以多多指正。


示例代码下载

源码地址(欢迎 Star,谢谢!)

还没看够?移步至:React Component 官网

相关文章
相关标签/搜索