Drcus | 王亚振

Drcus | 王亚振

随便写,记录点东西

React Lifecycle - 生命周期的管理艺术

发布于:  

对于 react 组件的生命周期无疑是核心概念之一。 今天看到了一篇从源码层级深入剖析内部机制原理的文章,觉得很好。 查看原文 感谢作者:twobin提供的干货

对于React 组件生命周期的迷惑, 这里就写下做个记录 。

组件的执行顺序

在自定义 React 组件时,根据需要会在组件生命周期的不同阶段实现不同的逻辑。

import React from 'react';
import ReactDom from 'react-dom';
import LifeCycle from 'react-lifecycle';

const body = document.body;

const MyComponent = React.createClass({
  mixins: [LifeCycle],

  render() {
    console.log('render');
    return null;
  }
});

ReactDom.render(<MyComponent />, body);
ReactDom.unmountComponentAtNode(body);
ReactDom.render(<MyComponent />, body);
ReactDom.render(<MyComponent />, body);

执行顺序:

  • 当首次装载组件时,按顺序执行 getDefaultPropsgetInitialStatecomponentWillMountrendercomponentDidMount

  • 当卸载组件时,执行 componentWillUnmount

  • 当重新装载组件时,此时按顺序执行 getInitialStatecomponentWillMountrendercomponentDidMount,但并不执行 getDefaultProps

  • 当再次渲染组件时,组件接受到更新状态,此时按顺序执行 componentWillReceivePropsshouldComponentUpdatecomponentWillUpdaterendercomponentDidUpdate

详解

主要通过 MOUNTINGRECEIVE_PROPSUNMOUNTING 这三种状态来管理生命周期, 三个状态对应三种方法,分别为:mountComponentupdateComponentunmountComponent,每个方法都提供了两种处理方法,will 方法在进入状态之前调用,did 方法在进入状态之后调用,三种状态三种方法五种处理方法

  • mountComponent -> MOUNTING
  • updateComponent -> RECEIVE_PROPS
  • unmountComponent -> UNMOUNTING

状态一 MOUNTING

mountComponent 负责管理生命周期中的 getInitialStatecomponentWillMountrendercomponentDidMount。 由于 getDefaultProps 是通过 Constructor 进行管理,因此也是整个生命周期中最先开始执行,而 mountComponent 只能望洋兴叹,无法调用到 getDefaultProps。这就解释了为何 getDefaultProps 只执行1次的原因。

首先通过 mountComponent 装载组件,此时,将状态设置为 MOUNTING,利用 getInitialState 获取初始化 state,初始化更新队列。

若存在 componentWillMount,则执行;如果此时在 componentWillMount 中调用 setState,是不会触发 reRender,而是进行 state 合并。

到此时,已经完成 MOUNTING 的工作,更新状态为 NULL,同时 state 也将执行更新操作,此刻在 render 中可以获取更新后的 this.state 数据。

mountComponent 本质上是通过 递归渲染 内容的,由于递归的特性,父组件的 componentWillMount 一定在其子组件的 componentWillMount 之前调用,而父组件的 componentDidMount 肯定在其子组件的 componentDidMount 之后调用。

当渲染完成之后,若存在 componentDidMount 则触发。这就解释了 componentWillMount - render - componentDidMount 三者之间的执行顺序。

状态二 RECEIVE_PROPS

updateComponent 负责管理生命周期中的 componentWillReceivePropsshouldComponentUpdatecomponentWillUpdaterendercomponentDidUpdate

首先通过 updateComponent 更新组件,如果前后元素不一致说明需要进行组件更新,此时将状态设置为RECEIVING_PROPS

若存在 componentWillReceiveProps,则执行;如果此时在 componentWillReceiveProps 中调用 setState,是不会触发 reRender,而是进行 state 合并。

到此时,已经完成 RECEIVING_PROPS 工作,更新状态为 NULL,同时 state 也将执行更新操作,此刻 this.state 可以获取到更新后的数据。

注意:此时 this.state 虽然获取到更新数据,但只能在内部源码中使用,我们在开发时,若在 componentWillReceiveProps 中调用 setState,那么在 componentWillReceivePropsshouldComponentUpdatecomponentWillUpdate 中还是无法获取到更新后的 this.state,即此时访问的this.state 仍然是未更新的数据,只有在 rendercomponentDidUpdate 中才能获取到更新后的this.state

执行顺序:componentWillReceiveProps -> componentWillUpdate -> render -> componentDidUpdate

注意:禁止shouldComponentUpdatecomponentWillUpdate 中调用 setState,会造成循环调用,直至耗光浏览器内存后崩溃

状态三 UNMOUNTING

unmountComponent 负责管理生命周期中的 componentWillUnmount

首先将状态设置为 UNMOUNTING,若存在 componentWillUnmount,则执行;如果此时在 componentWillUnmount 中调用 setState,是不会触发 reRender。更新状态为 NULL,完成组件卸载操作。

// 卸载组件
unmountComponent: function() {
  // 设置状态为 UNMOUNTING
  this._compositeLifeCycleState = CompositeLifeCycle.UNMOUNTING;

  // 如果存在 componentWillUnmount,则触发
  if (this.componentWillUnmount) {
    this.componentWillUnmount();
  }

  // 更新状态为 null
  this._compositeLifeCycleState = null;
  this._renderedComponent.unmountComponent();
  this._renderedComponent = null;

  ReactComponent.Mixin.unmountComponent.call(this);
}

setState 更新机制

当调用 setState 时,会对 state 以及 _pendingState 更新队列进行合并操作,但其实真正更新 state 的幕后黑手是replaceState

replaceState 会先判断当前状态是否为 MOUNTING,如果不是即会调用 ReactUpdates.enqueueUpdate 执行更新。

当状态不为 MOUNTINGRECEIVING_PROPS 时,performUpdateIfNecessary 会获取 _pendingElement_pendingState_pendingForceUpdate,并调用 updateComponent 进行组件更新。

如果在 shouldComponentUpdatecomponentWillUpdate 中调用 setState,此时的状态已经从 RECEIVING_PROPS -> NULL,则 performUpdateIfNecessary 就会调用 updateComponent 进行组件更新,但 updateComponent 又会调用 shouldComponentUpdatecomponentWillUpdate,因此造成循环调用,使得浏览器内存占满后崩溃。

// 更新 state
setState: function(partialState, callback) {
  // 合并 _pendingState
  this.replaceState(
    assign({}, this._pendingState || this.state, partialState),
    callback
  );
},

// 更新 state
replaceState: function(completeState, callback) {
  validateLifeCycleOnReplaceState(this);

  // 更新队列
  this._pendingState = completeState;

  // 判断状态是否为 MOUNTING,如果不是,即可执行更新
  if (this._compositeLifeCycleState !== CompositeLifeCycle.MOUNTING) {
    ReactUpdates.enqueueUpdate(this, callback);
  }
},

// 如果存在 _pendingElement、_pendingState、_pendingForceUpdate,则更新组件
performUpdateIfNecessary: function(transaction) {
  var compositeLifeCycleState = this._compositeLifeCycleState;

  // 当状态为 MOUNTING 或 RECEIVING_PROPS时,则不更新
  if (compositeLifeCycleState === CompositeLifeCycle.MOUNTING ||
      compositeLifeCycleState === CompositeLifeCycle.RECEIVING_PROPS) {
    return;
  }

  var prevElement = this._currentElement;
  var nextElement = prevElement;
  if (this._pendingElement != null) {
    nextElement = this._pendingElement;
    this._pendingElement = null;
  }

  // 调用 updateComponent
  this.updateComponent(
    transaction,
    prevElement,
    nextElement
  );
}

总结

  • React 通过三种状态:MOUNTING、RECEIVE_PROPS、UNMOUNTING,管理整个生命周期的执行顺序 ;

  • setState 会先进行 _pendingState 更新队列的合并操作,不会立刻 reRender,因此是异步操作,且通过判断状态(MOUNTING、RECEIVE_PROPS)来控制 reRender 的时机 ;

  • 不建议在 getDefaultPropsgetInitialStateshouldComponentUpdatecomponentWillUpdaterendercomponentWillUnmount 中调用 setState特别注意:不能在 shouldComponentUpdatecomponentWillUpdate中调用 setState,会导致循环调用。

厚颜一下 ~^_^~

赏赐