源码看React setState漫谈(二)

前端之家收集整理的这篇文章主要介绍了源码看React setState漫谈(二)前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

前面写过一篇setState漫谈(一)谈论了用户操作到页面渲染的过程,相信大家对React的setState机制有了一定了解。这里我们看看setState在生命周期的各个流程里调用都会发生什么。

更新流程

结论:

  1. componentWillReceiveProps中安心调用,对state的改变会被合并,并且只刷新一次。
  2. componentShouldUpdate,componentWillUpdate,render,componentDidUpdate中,可以调用,但是容易导致死循环,所以要做好条件判断。当然,如果非调用不可,官方只建议在componentDidUpdate调用

componentWillReceiveProps中调用setState

还是先放上流程图:

首先我们知道React是在ReactUpdatesFlushTransaction事务中进行更新操作的。
该事务perform之前会先报错dirtyComponents的length。
perform之后会将this.dirtyComponents长度与之前保存的进行对比。如果长度不一样,表示在这一轮更新中有setState被触发,新的dirtyComponent被加入序列。然后删除这一轮更新的dirtyComponent,重新flushBatchedUpdates更新新加入的。
源码如下:

var NESTED_UPDATES = {
  initialize: function() {
    this.dirtyComponentsLength = dirtyComponents.length;
  },close: function() {
    if (this.dirtyComponentsLength !== dirtyComponents.length) {
      dirtyComponents.splice(0,this.dirtyComponentsLength);
      flushBatchedUpdates();
    } else {
      dirtyComponents.length = 0;
    }
  },};
Object.assign(ReactUpdatesFlushTransaction.prototype,Transaction,{
  getTransactionWrappers: function() {
    return TRANSACTION_WRAPPERS;
  },destructor: function() {
   .....
  },perform: function(method,scope,a) {
    ......
  },});

然后我们看第一次更新都发生了什么?
首先会判断是否需要更新。

performUpdateIfNecessary: function (transaction) {
    if (this._pendingElement != null) {
      ReactReconciler.receiveComponent(this,this._pendingElement,transaction,this._context);
    } else if (this._pendingStateQueue !== null || this._pendingForceUpdate) {
      this.updateComponent(transaction,this._currentElement,this._context,this._context);
    } else {
      this._updateBatchNumber = null;
    }
  },

第一次进来this._pendingStateQueue有值,所以进入更新,调用updateComponent

updateComponent: function (transaction,prevParentElement,nextParentElement,prevUnmaskedContext,nextUnmaskedContext) {
    var inst = this._instance;

    var willReceive = false;
    var nextContext;

    var prevProps = prevParentElement.props;
    var nextProps = nextParentElement.props;

    ....
    inst.componentWillReceiveProps(nextProps,nextContext);    //将新的state合并到更新队列中,此时nextState为最新的state
    var nextState = this._processPendingState(nextProps,nextContext);
    var shouldUpdate = true;

    if (!this._pendingForceUpdate) {
      shouldUpdate = inst.shouldComponentUpdate(nextProps,nextState,nextContext);
      } 

    this._updateBatchNumber = null;
    if (shouldUpdate) {
      this._pendingForceUpdate = false;
      this._performComponentUpdate(nextParentElement,nextProps,nextContext,nextUnmaskedContext);
    }
},

我们看到进入updateComponent,然后执行componentWillReceiveProps,
componentWillReceiveProps会调用setState方法

setState,更新了pendIngStateQueue,更新了dirtyComponents。然后接着走updateComponent
我们看到执行了

var nextState = this._processPendingState(nextProps,nextContext);

_processPendingState: function (props,context) {
    var inst = this._instance;
    var queue = this._pendingStateQueue;
    var replace = this._pendingReplaceState;
    this._pendingReplaceState = false;
    this._pendingStateQueue = null;

    if (!queue) {
      return inst.state;
    }

    if (replace && queue.length === 1) {
      return queue[0];
    }

    var nextState = _assign({},replace ? queue[0] : inst.state);

    return nextState;
  },

可以看到这里根据pendingStateQueue,更新了state并赋给了nextState,同时删除了pendingStateQueue

接下来就是componentShouldUpdate,componentDidUpdate这些生命周期函数。perform执行完毕。
接着是transaction的close方法。上面我们已经介绍过,因为updateComponent内部调用setState,导致dirtyComponent变了,因此又执行一轮flushBatchedUpdates
接着又到了判断的逻辑

performUpdateIfNecessary: function (transaction) {
    if (this._pendingStateQueue !== null || this._pendingForceUpdate) {
      this.updateComponent(transaction,

因为上面_pendingStateQueue已经被删除,所以这次是不会触发新一轮的update.

coponentShouldUpdate,componentWillUpdate,render和componentDidUpdate

这几个和componentWIllReceiveProps有什么区别?最大的区别就在于。他们都是在_processPendingState方法之后调用的。
其他逻辑差不多,调用setState更新了pendIngStateQueue,更新了dirtyComponents....
但是这里会到updateComponent方法以后没有删除‘pendingStateQueue’!
所以在close方法中,执行新一轮flushBatchedUpdates时,再次判断performUpdateIfNecessary是需要更新的,因此又会触发循环。这就造成了死循环!

细节补充

也许有人会问为什么setState对_pendingStateQueue的更新会同步到ReactCompositeComponent里面。那我们就来看看

首先,_pendingStateQueue来自enqueueReplaceState

var internalInstance = getInternalInstanceReadyForUpdate(publicInstance);
internalInstance._pendingStateQueue = [completeState];
internalInstance._pendingReplaceState = true;

其实这里的internalInstance就是ReactElement对象。一路跟着代码跟到ReactReconciler,一直到

internalInstance.performUpdateIfNecessary(transaction);

可我们发现internalInstance并没有performUpdateIfNecessary方法啊,其实这是定义在原型上的方法。我们在instantiateReactComponent中发现了端倪:

Object.assign(
  ReactCompositeComponentWrapper.prototype,ReactCompositeComponent,{
    _instantiateReactComponent: instantiateReactComponent,},);

所以,composite的调用者就是internalInstance,也就是我们在调用栈里传来传去的component,而我们从头到尾维护的也是internalInstance的属性

原文链接:https://www.f2er.com/react/302850.html

猜你在找的React相关文章