一个关于React.Component.setState的问题
React组件重新渲染的条件是: B.只要调用this.setState()就会发生重新渲染。 C.必须调用this.setState()且传递不同于当前this.setState()的参数,才会引发重新渲染。 本文将从三方面说明这个问题为什么选择C。或者说为什么 结论我还是想选择B 引用规范TL;DR
React原文中关于setState的说明: setState(updater[,callback]) 实验验证设计实验来验证 基于 React16( 引入了 Fiber 架构)和 React 0.14 分别进行实验。至于React 15的问题,留给读者自己吧。 编写如下组件代码: class A extends React.Component{ constructor(props){ super(props); this.state = { a:1 } this._onClick = this.onClick.bind(this); } onClick(){ this.setState({a:2}) // 替换点 } render(){ console.log('rerender'); return( <div onClick={this._onClick}> <p>a: {this.state.a}</p> <p>{Math.random()}</p> </div> ); } } 如果需要可以读者自行粘贴重新复现实验。
更新的标准:界面中显示的随机数是否发生了变化。当然也可以观察
可见对于 在 在 源码说明React 16中是这样的:https://github.com/facebook/r... 1. const payload = update.payload; 2. let partialState; 3. if (typeof payload === 'function') { 4. partialState = payload.call(instance,prevState,nextProps); 5. } else { 6. // Partial state object 7. partialState = payload; 8. } 9. if (partialState === null || partialState === undefined) { 10. // Null and undefined are treated as no-ops. 11. return prevState; 12.} 13.// Merge the partial state and the previous state. 14.return Object.assign({},partialState); React 14中是这样的:证有容易,证无难,所以我要顺着整条链路的源码的展示一遍。 TL;DR var nextState = assign({},replace ? queue[0] : inst.state); for (var i = replace ? 1 : 0; i < queue.length; i++) { var partial = queue[i]; assign(nextState,typeof partial === 'function' ? partial.call(inst,nextState,props,context) : partial); } return nextState; 1.调用
2.原型方法 ReactComponent.prototype.setState = function (partialState,callback) { this.updater.enqueueSetState(this,partialState); if (callback) { this.updater.enqueueCallback(this,callback); } }; 3.入队方法 enqueueSetState: function (publicInstance,partialState) { var internalInstance = getInternalInstanceReadyForUpdate(publicInstance,'setState'); if (!internalInstance) { return; } var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []); queue.push(partialState); enqueueUpdate(internalInstance); }, internalInstance 是一个 ReactCompositeComponentWrapper,大概就是包装着ReactComponent实例的一个对象。 4.入队 function enqueueUpdate(internalInstance) { ReactUpdates.enqueueUpdate(internalInstance); } 5.更具当前的批量策略来决定更新方法 function enqueueUpdate(component) { ensureInjected(); if (!batchingStrategy.isBatchingUpdates) { batchingStrategy.batchedUpdates(enqueueUpdate,component); return; } dirtyComponents.push(component); } 6.可以看到直到这里都没人管这个东西到底更新的是什么。 ReactUpdates.batchedUpdates(handleTopLevelImpl,bookKeeping); 9.请求更新队列,进行更新 var flushBatchedUpdates = function () { // ReactUpdatesFlushTransaction's wrappers will clear the dirtyComponents // array and perform any updates enqueued by mount-ready handlers (i.e.,// componentDidUpdate) but we need to check here too in order to catch // updates enqueued by setState callbacks and asap calls. while (dirtyComponents.length || asapEnqueued) { if (dirtyComponents.length) { var transaction = ReactUpdatesFlushTransaction.getPooled(); transaction.perform(runBatchedUpdates,null,transaction);<!--here--> ReactUpdatesFlushTransaction.release(transaction); } if (asapEnqueued) { asapEnqueued = false; var queue = asapCallbackQueue; asapCallbackQueue = CallbackQueue.getPooled(); queue.notifyAll(); CallbackQueue.release(queue); } } }; 10.更新 function runBatchedUpdates(transaction) { var len = transaction.dirtyComponentsLength; // Since reconciling a component higher in the owner hierarchy usually (not // always -- see shouldComponentUpdate()) will reconcile children,reconcile // them before their children by sorting the array. dirtyComponents.sort(mountOrderComparator); for (var i = 0; i < len; i++) { // If a component is unmounted before pending changes apply,it will still // be here,but we assume that it has cleared its _pendingCallbacks and // that performUpdateIfNecessary is a noop. var component = dirtyComponents[i]; // If performUpdateIfNecessary happens to enqueue any new updates,we // shouldn't execute the callbacks until the next render happens,so // stash the callbacks first var callbacks = component._pendingCallbacks; component._pendingCallbacks = null; <!--here--> ReactReconciler.performUpdateIfNecessary(component,transaction.reconcileTransaction); if (callbacks) { for (var j = 0; j < callbacks.length; j++) { transaction.callbackQueue.enqueue(callbacks[j],component.getPublicInstance()); } } } } 11.最重要的来了 performUpdateIfNecessary: function (transaction) { if (this._pendingElement != null) { ReactReconciler.receiveComponent(this,this._pendingElement || this._currentElement,transaction,this._context); } if (this._pendingStateQueue !== null || this._pendingForceUpdate) { this.updateComponent(transaction,this._currentElement,this._context,this._context); } }, 12.更新 updateComponent: function (transaction,prevParentElement,nextParentElement,prevUnmaskedContext,nextUnmaskedContext) { //... props context 更新 var nextState = this._processPendingState(nextProps,nextContext); var shouldUpdate = this._pendingForceUpdate || !inst.shouldComponentUpdate || inst.shouldComponentUpdate(nextProps,nextContext); if (shouldUpdate) { this._pendingForceUpdate = false; // Will set `this.props`,`this.state` and `this.context`. this._performComponentUpdate(nextParentElement,nextProps,nextContext,nextUnmaskedContext); } else { // If it's determined that a component should not update,we still want // to set props and state but we shortcut the rest of the update. this._currentElement = nextParentElement; this._context = nextUnmaskedContext; inst.props = nextProps; inst.state = nextState; inst.context = nextContext; } }, 13.计算state _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); for (var i = replace ? 1 : 0; i < queue.length; i++) { var partial = queue[i]; assign(nextState,context) : partial); } return nextState; }, 14.就这样了。 var nextState = assign({},context) : partial); } return nextState; 15.流程中没有任何比较操作。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |