前面写过一篇setState漫谈(一)谈论了用户操作到页面渲染的过程,相信大家对React的setState机制有了一定了解。这里我们看看setState在生命周期的各个流程里调用都会发生什么。
更新流程
结论:
-
componentWillReceiveProps
中安心调用,对state的改变会被合并,并且只刷新一次。 -
componentShouldUpdate
,componentWillUpdate
,render
,componentDidUpdate
中,可以调用,但是容易导致死循环,所以要做好@R_404_390@。当然,如果非调用不可,官方只建议在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的属性