React的状态更新

当一个ReactCompositeComponent调用setState时,组件就会开始更新,其背后的机制到底是怎样的呢?先从熟悉的setState来,还是先看源码


 setState: function(partialState) {
    // Merge with `_pendingState` if it exists, otherwise with existing state.
    this.replaceState(merge(this._pendingState || this.state, partialState));
  }

可见,setState方法只是做了一步预处理,将传入的partialState和当前的(pending)state进行了合并,然后再调用了replaceState方法。核心的主要内容还是在replaceState中。

replaceState方法里,首先对组件当前的状态进行了断言。ComponentLifeCycle必须为MOUNTED或者CompositeLifeCycle为MOUNTING,且CompositeLifeCycle不能为RECEIVING_STATE或者UNMOUNTING(注:ComponentLifeCycle是在ReactComponent中定义的,而后者是在ReactCompositeComponent中定义的)。

断言通过以后,从事务池中取出一个事务。(关于事务的更多信息,可以查看我之前的文章https://www.hoyt-tian.com/reactde-shi-wu-ji-zhi/ ),然后在事务中完成this._receivePropsAndState方法,这个方法接受三个参数,其源码如下:


_receivePropsAndState: function(nextProps, nextState, transaction) {
    if (!this.shouldComponentUpdate ||
        this.shouldComponentUpdate(nextProps, nextState)) {
      // Will set `this.props` and `this.state`.
      this._performComponentUpdate(nextProps, nextState, transaction);
    } else {
      // If it's determined that a component should not update, we still want
      // to set props and state.
      this.props = nextProps;
      this.state = nextState;
    }
  }

从上面的代码可以看到,如果定义了shouldComponentUpdate方法,那么此时就会被调用了,当满足更新条件时,才会通过_performComponentUpdate来真正进行组件更新,否则就只会更新props和state,等待下一次重绘。这也是为什么React的官网会说,setState并不保证每一次都会重绘组件。

再深入到_performComponentUpdate一探究竟:


 _performComponentUpdate: function(nextProps, nextState, transaction) {
    var prevProps = this.props;
    var prevState = this.state;

    if (this.componentWillUpdate) {
      this.componentWillUpdate(nextProps, nextState, transaction);
    }

    this.props = nextProps;
    this.state = nextState;

    this.updateComponent(transaction);

    if (this.componentDidUpdate) {
      transaction.getReactOnDOMReady().enqueue(
        this,
        this.componentDidUpdate.bind(this, prevProps, prevState)
      );
    }
  }

在这个方法里面,会调用componentWillUpdate,如果有这个方法的话;同时还会调用updateComponent方法来进行组件更新。最后,如果有componentDidUpdate方法的话,会被添加到ReactOnDOMReady的实例上(这个类也有缓冲池机制),等待DOM更新结束后被调用。说了这么久,终于要真正开始进入组件更新的代码了,它就在updateComponent中。


 updateComponent: function(transaction) {
    var currentComponent = this._renderedComponent;
    var nextComponent = this._renderValidatedComponent();
    if (currentComponent.constructor === nextComponent.constructor) {
      if (!nextComponent.props.isStatic) {
        currentComponent.receiveProps(nextComponent.props, transaction);
      }
    } else {
      // These two IDs are actually the same! But nothing should rely on that.
      var thisID = this._rootNodeID;
      var currentComponentID = currentComponent._rootNodeID;
      currentComponent.unmountComponent();
      var nextMarkup = nextComponent.mountComponent(thisID, transaction);
      ReactComponent.DOMIDOperations.dangerouslyReplaceNodeWithMarkupByID(
        currentComponentID,
        nextMarkup
      );
      this._renderedComponent = nextComponent;
    }
  },

在_renderValidatedComponent方法中会真正调用render方法,获得新生成的nextComponent,随后,通过currentComponent和nextComponent两者的构造函数来判断类型是否发生了变化。如果两者不一样,就将currentComponent执行unmount,并将nextComponent挂在到当前组件上。如果currentComponent和nextComponent两者类型是一致的,说明还是原来的组件,只需要更新props和state即可,源码中还有一个判断条件nextComponent.props.isStatic,换言之,如果给一个组件的props中添加了isStatic,那么它就不会进行属性状态更新,多数情况下,currentComponent还是会进入到receiveProps,触发currentComponent自身的更新。

整个流程用下图进行总结(单击查看大图):
setState执行流程图

Show Comments

Get the latest posts delivered right to your inbox.