React生命周期函数

206 阅读6分钟

16之前旧的生命周期:

在这里插入图片描述

16之后新的生命周期:

官方生命周期图

一、挂载期间:只调用一次

组件被实例化并挂载在到DOM树这一过程称为装载,在装载期调用的生命周期函数依次为

  • **constructor:**初始化状态,进行函数的this绑定,只调用一次, 

如果您不初始化状态并且不绑定方法,则无需为React组件实现构造函数。

但是若在构造函数中未调用super(props)this.props将在构造函数中未定义,造成错误。

作用:

  • 通过将对象分配给来初始化本地状态 state。

  • 给事件(方法)绑定this, 也就是把事件和方法绑定到创建好的组件实例上。

  • getDefaultProps()

设置默认的props,也可以用dufaultProps设置组件的默认属性。 *** getInitialState()** 初始化state,可以直接在constructor中定义this.state

  • **componentWillMount():**组件初始渲染(render()被调用前)前调用,并且仅调用一次.可以修改state
  • **render():  **创建虚拟dom,进行diff算法。
  • **componentDidMount():挂载组件(插入树中)后立即调用,**组件第一次渲染完成,此时dom节点已经生成,可以在这里调用ajax请求,返回数据setState后组件会重新渲染,只调用一次

二、更新阶段(updateing)

  • componentWillRecivedProps() : 老版本使用, 仅在父级更新导致重新渲染时才触发。

通过对比nextProps和this.props,将nextProps的state为当前组件的state,从而重新渲染组件。,一是根据 props 来更新 state,二是触发一些回调,如动画或页面跳转等。

  //通过this.props来获取旧的外部状态,初始 props 不会被调用
    //通过对比新旧状态,来判断是否执行如this.setState及其他方法
   componentWillReceiveProps (nextProps) {
        nextProps.openNotice !== this.props.openNotice&&this.setState({
            openNotice:nextProps.openNotice
        },() => {
            console.log(this.state.openNotice:nextProps)
            //将state更新为nextProps,在setState的第二个参数(回调)可以打印出新的state
        })
    }
  • shouldComponentUpdate(nextProps, nextState):是否更新

组件挂载后(即执行完render),接收到新的state或props时被调用。返回true代表允许更新,返回false代表不允许更新。则不会走下面的更新生命周期函数。

  • componentWillUpdate():重新渲染

     shouldComponentUpdate返回true以后,组件进入重新渲染的流程,进入componentWillUpdate,这里同样可以拿到nextProps和nextState。

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

  • render():更新dom树

在此react会通过其diff算法比较更新前后的新旧DOM树,比较以后,找到最小的有差异的DOM节点,并重新渲染。

  • componentDidUpdate(prevProps,prevState):更新发生(完成后)后立即调用。

你可以在这个方法中进行DOM操作,或者**做一些异步调用。**这个和首次装载过程后调用componentDidMount是类似的,不一样的是你可能需要判断下属性是否变化了再发起网络请求

三、卸载阶段Unmount()

卸载阶段:指组件从DOM树种移除。

  • componentWillUnmount() : 组件将要挂载

你可以在这个函数中进行相关清理工作,比如删除定时器。但是不能操作state

componentWillUnmount() {
    console.log('componentWillUnmount');

    // 清除timer
    clearInterval(this.timerID1);
    clearTimeout(this.timerID2);

    // 关闭socket
    this.myWebsocket.close();

    // 取消消息订阅...
  }

四、错误捕获

  • componentDidCatch(error, info):捕获错误

   在react组件中如果产生的错误没有被被捕获会被抛给上层组件,如果上层也不处理的话就会抛到顶层导致浏览器白屏错误,在React16中我们可以实现这个方法来捕获子组件产生的错误,然后在父组件中妥善处理,比如搞个弹层通知用户网页崩溃等。

//error引发的错误, info具有componentStack键的对象,其中包含有关哪个组件引发了错误的信息
    componentDidCatch(error, info) {
        // Example "componentStack":
        //   in ComponentThatThrows (created by App)
        //   in ErrorBoundary (created by App)
        //   in div (created by App)
        //   in App
        logComponentStackToMyService(info.componentStack);
    }

React16中的生命周期函数变化:

componentWillMount,componentWillUpdate, 

componentWillReceiveProps 被以下生命周期替代。

新增:

getDerivedStateFromProps(nextProps, prevState):   替代componentWillReceiveProps

static getDerivedStateFromProps(nextProps,prevState){
     //该方法内禁止访问this
     if(nextProps.email !== prevState.email){
     //通过对比nextProps和prevState,返回一个用于更新状态的对象
     return {
                value:nextProps.email
     }
     }
     //不需要更新状态,返回null
     return null
}

这两者最大的不同就是:在 componentWillReceiveProps 中,我们一般会做以下两件事,一是根据 props 来更新 state,二是触发一些回调,如动画或页面跳转等。

   在老版本的 React 中,这两件事我们都需要在 componentWillReceiveProps 中去做。 

如:

class ExampleComponent extends React.Component {
  state = {
    isScrollingDown: false,
  };

  componentWillReceiveProps(nextProps) {
    if (this.props.currentRow !== nextProps.currentRow) {
      // 检测到变化后更新状态、并请求数据
      this.setState({
        isScrollingDown: nextProps.currentRow > this.props.currentRow,
      });
      this.loadAsyncData()
    }
  }

  loadAsyncData() {/* ... */}
}

而在新版本中,官方将更新 state 与触发回调重新分配到了 getDerivedStateFromProps 与 componentDidUpdate 中,使得组件整体的更新逻辑更为清晰。而且在 getDerivedStateFromProps 中还禁止了组件去访问 this.props,强制让开发者去比较 nextProps 与 prevState 中的值,以确保当开发者用到 getDerivedStateFromProps 这个生命周期函数时,就是在根据当前的 props 来更新组件的 state,而不是去做其他一些让组件自身状态变得更加不可预测的事情。

如: getDerivedStateFromProps 是否更新, componentDidUpdate做需要更新的操作

class ExampleComponent extends React.Component {
  state = {
    isScrollingDown: false,
    lastRow: null,
  };

  static getDerivedStateFromProps(nextProps, prevState) {
    // 不再提供 prevProps 的获取方式
    if (nextProps.currentRow !== prevState.lastRow) {
      return {
        isScrollingDown: nextProps.currentRow > prevState.lastRow,
        lastRow: nextProps.currentRow,
      };
    }

    // 默认不改动 state
    return null;
  }

  componentDidUpdate() {
    // 仅在更新触发后请求数据
    this.loadAsyncData()
  }

  loadAsyncData() {/* ... */}
}

getSnapshotBeforeUpdate(prevProps, prevState)

作为componentDidUpdate的第三个参数。该函数与 componentDidUpdate 一起使用可以取代 componentWillUpdate 的所有功能。

这两者的区别在于:

  在 React 开启异步渲染模式后,在 render 阶段读取到的 DOM 元素状态并不总是和 commit 阶段相同,这就导致在componentDidUpdate 中使用 componentWillUpdate 中读取到的 DOM 元素状态是不安全的,因为这时的值很有可能已经失效了。

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

该方法的触发时间为update发生的时候,在render之后dom渲染之前返回一个值,作为componentDidUpdate的第三个参数。该函数与 componentDidUpdate 一起使用可以取代 componentWillUpdate 的所有功能,比如以下是官方的例子:

常用生命周期函数:

  • constructor: 初始化状态,进行函数绑定

  • componentDidMount: 进行DOM操作,进行异步调用初始化页面

  • componentWillReceiveProps: 根据props更新状态

  • componentWillUnmount: 清理组件定时器,网络请求或者相关订阅等