react 生命周期

react 生命周期

Posted by SkioFox on September 15, 2018

React 生命周期(v16.4)

React v16.4 的生命周期

avatar

React v16.4 的生命周期

变更缘由

原来(React v16.0前)的生命周期在React v16推出的Fiber之后就不合适了,因为如果要开启async rendering,在render函数之前的所有函数,都有可能被执行多次。

原来(React v16.0前)的生命周期有哪些是在render前执行的呢?

  • componentWillMount
  • componentWillReceiveProps
  • shouldComponentUpdate
  • componentWillUpdate

如果开发者开了async rendering,而且又在以上这些render前执行的生命周期方法做AJAX请求的话,那AJAX将被无谓地多次调用。。。明显不是我们期望的结果。而且在componentWillMount里发起AJAX,不管多快得到结果也赶不上首次render,而且componentWillMount在服务器端渲染也会被调用到(当然,也许这是预期的结果),这样的IO操作放在componentDidMount里更合适。

禁止不能用比劝导开发者不要这样用的效果更好,所以除了shouldComponentUpdate,其他在render函数之前的所有函数(componentWillMount,componentWillReceiveProps,componentWillUpdate)都被getDerivedStateFromProps替代。

也就是用一个静态函数getDerivedStateFromProps来取代被deprecate的几个生命周期函数,就是强制开发者在render之前只做无副作用的操作,而且能做的操作局限在根据props和state决定新的state

React v16.0刚推出的时候,是增加了一个componentDidCatch生命周期函数,这只是一个增量式修改,完全不影响原有生命周期函数;但是,到了React v16.3,大改动来了,引入了两个新的生命周期函数。

新引入了两个新的生命周期函数:getDerivedStateFromPropsgetSnapshotBeforeUpdate

getDerivedStateFromProps

getDerivedStateFromProps本来(React v16.3中)是只在创建和更新(由父组件引发部分),如果不是由父组件引发,那么getDerivedStateFromProps也不会被调用,如自身setState引发或者forceUpdate引发。

React v16.3 的生命周期图

avatar

React v16.3

这样的话理解起来有点乱,在React v16.4中改正了这一点,让getDerivedStateFromProps无论是Mounting还是Updating,也无论是因为什么引起的Updating,全部都会被调用,具体可看React v16.4 的生命周期图。

React v16.4后的getDerivedStateFromProps

static getDerivedStateFromProps(props, state) 在组件创建时和更新时的render方法之前调用,它应该返回一个对象来更新状态,或者返回null来不更新任何内容。

getSnapshotBeforeUpdate

getSnapshotBeforeUpdate() 被调用于render之后,可以读取但无法使用DOM。它使您的组件可以在可能更改之前从DOM捕获一些信息(例如滚动位置)。此生命周期返回的任何值都将作为参数传递给componentDidUpdate()。

官网给的例子:

class ScrollingList extends React.Component {
  constructor(props) {
    super(props);
    this.listRef = React.createRef();
  }

  getSnapshotBeforeUpdate(prevProps, prevState) {
    //我们是否要添加新的 items 到列表?
    // 捕捉滚动位置,以便我们可以稍后调整滚动.
    if (prevProps.list.length < this.props.list.length) {
      const list = this.listRef.current;
      return list.scrollHeight - list.scrollTop;
    }
    return null;
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    //如果我们有snapshot值, 我们已经添加了 新的items.
    // 调整滚动以至于这些新的items 不会将旧items推出视图。
    // (这边的snapshot是 getSnapshotBeforeUpdate方法的返回值)
    if (snapshot !== null) {
      const list = this.listRef.current;
      list.scrollTop = list.scrollHeight - snapshot;
    }
  }

  render() {
    return (
      <div ref={this.listRef}>{/* ...contents... */}</div>
    );
  }

经典的react生命周期流程图

左边是单个组件生命周期,右边是多组件交互时组件的生命周期。

avatar

  • 十个生命周期包含:

    初始化阶段

    • getDefaultProps
    • getInitialState
    • componentWillMount
    • render
    • componentDidMount

    运行中状态阶段

    • componentWillReciveProps
    • shouldComponentUpdate
    • componentWillUpdate
    • componentDidUpdate

    销毁阶段

    • componentWillUnmount

avatar avatar

    // parent
    import React,{Component} from 'react'
    import Child from './Child'
    import {Button,Input} from 'antd'
    import './index.less'
    export default class Life extends Component{
        // constructor(props) {
        //     super(props);
        //     this.state = {
        //         count:0
        //     };
        // }
        // 等价于上面
        state = {
            count:0
        }
        // 箭头函数this指向组件实例
        handleAdd=()=>{
            this.setState({
                count: this.state.count + 1
            })
        }
        // 非箭头函数This指向取决于调用者,这里会指向dom对象
        handleClick(){
            this.setState({
                count: this.state.count + 1
            })
        }

        render(){
            // react里面style=,外面括号是react语法,内部括号代表是对象
            let style = {
                padding:200
            }
            return <div className="content" style={style}>
                <p>React生命周期介绍</p>
                <Input></Input>
                <Button onClick={this.handleAdd} type="primary">AntD点击一下</Button>
                <button onClick={this.handleAdd}>点击一下</button>
                <button onClick={this.handleClick.bind(this)}>点击一下</button>
                <p>{this.state.count}</p>
                <Child name={this.state.count}></Child>
            </div>
        }
    }
    // child
    import React from 'react'
    export default class Child extends React.Component {
        constructor(props) {
            super(props);
            // state只是用于组件自身管理状态
            this.state = {
                count: 0
            };
        }
        componentWillMount(){
            console.log('will mount');
        }

        componentDidMount(){
            console.log('did mount');
        }

        componentWillReceiveProps(newProps){
            console.log('will props' + newProps.name)
        }

        shouldComponentUpdate(){
            console.log('should upate')
            return true;
        }

        componentWillUpdate(){
            console.log('will upate')
        }

        componentDidUpdate(){
            console.log('did upate')
        }

        render(){
            return <div>
                <p>这里是子组件,测试子组件的生命周期</p>
                <p>{this.props.name}</p>
            </div>
        }
    }