[React技术内幕]:setState的秘密
对于大多数的React开发者,setState可能是最常用的API之一。React作为View层,通过改变data从而引发UI的更新。React不像Vue这种MVVM库,直接修改data并不能视图的改变,更新状态(state)的过程必须使用setState。 setState介绍setState的函数签名如下: setState(partialState,callback) 我们看到setState接受两个参数,一个是 this.setState({ value: this.state.value + 1 }) 上面这个例子执行的结果是将state中value的值增加1。但事实真的如此简单吗?我们看下面的代码: class Example extends React.Component { constructor(props) { super(props); } state = { value: 0 } render() { return ( <div> <div>The Value: {this.state.value}</div> <button onClick={::this._addValue}>add Value</button> </div> ); } _addValue() { this.setState({ value: this.state.value + 1 }) this.setState({ value: this.state.value + 1 }) } } 如果你认为点击"addValue"按妞时每次会增加2的话,说明你可能对setState不是很了解。事实上如果你真的需要每次增加2的话,你的 _addValue() { this.setState((preState,props)=>({ value: preState.value + 1 })) this.setState((preState,props)=>({ value: preState.value + 1 })) } 我们可以看到其实参数 _addValue() { setTimeout(()=>{ this.setState({ value: this.state.value + 1 }); this.setState({ value: this.state.value + 1 }); },0) } 你现在是否眉头一皱,发现setState并没有这么简单。 关于setState的介绍,官方文档是这么介绍的:
as they may eventually be batched together. You can provide an optional 翻译过来(意译)相当于:
通篇几个字眼让我们很难办,不保证、可能,到底什么时候才会同步更新,什么时候才会异步更新?可能真的需要我们研究一下。 setState的实现 function ReactComponent(props,context,updater) { this.props = props; this.context = context; this.refs = emptyObject; this.updater = updater || ReactNoopUpdateQueue; } ReactComponent.prototype.setState = function (partialState,callback) { this.updater.enqueueSetState(this,partialState); if (callback) { this.updater.enqueueCallback(this,callback); } }; 我们首先看setState,首先调用的是 this.updater = updater || ReactNoopUpdateQueue; 没有传入参数 constructor(props) { super(props); //这是指个演示,this.isMounted函数已经被废弃 console.log(this.updater.isMounted()) this.setState({ value: 1 }) }
上面的警告就是 inst.updater = ReactUpdateQueue; 那我们来看看 var ReactUpdatedQueue = { enqueueSetState: function (publicInstance,partialState) { var internalInstance = getInternalInstanceReadyForUpdate(publicInstance,'setState'); if (!internalInstance) { return; } var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []); queue.push(partialState); enqueueUpdate(internalInstance); },} 我们通过
通过执行函数 var internalInstance = getInternalInstanceReadyForUpdate(publicInstance,'setState'); 我们得到的 var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []); queue.push(partialState); 如果 function enqueueUpdate(internalInstance) { ReactUpdates.enqueueUpdate(internalInstance); } var ReactUpdates = { enqueueUpdate: function enqueueUpdate(component) { ensureInjected(); if (!batchingStrategy.isBatchingUpdates) { batchingStrategy.batchedUpdates(enqueueUpdate,component); return; } dirtyComponents.push(component); } } 首先执行的 var ReactDefaultBatchingStrategy = { isBatchingUpdates: false,batchedUpdates: function(callback,a,b,c,d,e) { var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates; ReactDefaultBatchingStrategy.isBatchingUpdates = true; if (alreadyBatchingUpdates) { callback(a,e); } else { transaction.perform(callback,null,e); } },}; Transaction关于React中的事务Transaction,源码中给出了下面的ASCII图: /** * <pre> * wrappers (injected at creation time) * + + * | | * +-----------------|--------|--------------+ * | v | | * | +---------------+ | | * | +--| wrapper1 |---|----+ | * | | +---------------+ v | | * | | +-------------+ | | * | | +----| wrapper2 |--------+ | * | | | +-------------+ | | | * | | | | | | * | v v v v | wrapper * | +---+ +---+ +---------+ +---+ +---+ | invariants * perform(anyMethod) | | | | | | | | | | | | maintained * +----------------->|-|---|-|---|-->|anyMethod|---|---|-|---|-|--------> * | | | | | | | | | | | | * | | | | | | | | | | | | * | | | | | | | | | | | | * | +---+ +---+ +---------+ +---+ +---+ | * | initialize close | * +-----------------------------------------+ * </pre> */ var RESET_BATCHED_UPDATES = { initialize: emptyFunction,close: function() { ReactDefaultBatchingStrategy.isBatchingUpdates = false; },}; var FLUSH_BATCHED_UPDATES = { initialize: emptyFunction,close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates),}; var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES,RESET_BATCHED_UPDATES]; function ReactDefaultBatchingStrategyTransaction() { this.reinitializeTransaction(); } Object.assign( ReactDefaultBatchingStrategyTransaction.prototype,Transaction.Mixin,{ getTransactionWrappers: function() { return TRANSACTION_WRAPPERS; },} ); var transaction = new ReactDefaultBatchingStrategyTransaction(); 其中wrapper setState更新的过程 我们再次回顾一下更新的过程,如果处于批量更新的过程中(即isBatchingUpdates为 //代码有省略 var flushBatchedUpdates = function() { while (dirtyComponents.length) { if (dirtyComponents.length) { var transaction = ReactUpdatesFlushTransaction.getPooled(); transaction.perform(runBatchedUpdates,transaction); ReactUpdatesFlushTransaction.release(transaction); } //...... } }; 我们发现在函数 function runBatchedUpdates(transaction) { var len = transaction.dirtyComponentsLength; dirtyComponents.sort(mountOrderComparator); for (var i = 0; i < len; i++) { var component = dirtyComponents[i]; var callbacks = component._pendingCallbacks; component._pendingCallbacks = null; //..... ReactReconciler.performUpdateIfNecessary(component,transaction.reconcileTransaction); //....... if (callbacks) { for (var j = 0; j < callbacks.length; j++) { transaction.callbackQueue.enqueue( callbacks[j],component.getPublicInstance() ); } } } } 首先函数将 performUpdateIfNecessary: function (internalInstance,transaction) { internalInstance.performUpdateIfNecessary(transaction); } var ReactCompositeComponentMixin = { performUpdateIfNecessary: function(transaction) { //...... if (this._pendingStateQueue !== null || this._pendingForceUpdate) { this.updateComponent( transaction,this._currentElement,this._context,this._context ); } } } 上面代码是 updateComponent: function( transaction,prevParentElement,nextParentElement,prevUnmaskedContext,nextUnmaskedContext ) { var inst = this._instance; var willReceive = false; var nextContext; var nextProps; // 验证组件context是否改变 // ...... // 验证是否是props更新还是组件state更新 if (prevParentElement === nextParentElement) { nextProps = nextParentElement.props; } else { //存在props的更新 nextProps = this._processProps(nextParentElement.props); willReceive = true; } //根据条件判断是否调用钩子函数componentWillReceiveProps if (willReceive && inst.componentWillReceiveProps) { inst.componentWillReceiveProps(nextProps,nextContext); } //计算新的state var nextState = this._processPendingState(nextProps,nextContext); var shouldUpdate = this._pendingForceUpdate || !inst.shouldComponentUpdate || inst.shouldComponentUpdate(nextProps,nextState,nextContext); if (shouldUpdate) { this._pendingForceUpdate = false; this._performComponentUpdate( nextParentElement,nextProps,nextContext,transaction,nextUnmaskedContext ); } else { this._currentElement = nextParentElement; this._context = nextUnmaskedContext; inst.props = nextProps; inst.state = nextState; inst.context = 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 = Object.assign({},replace ? queue[0] : inst.state); for (var i = replace ? 1 : 0; i < queue.length; i++) { var partial = queue[i]; Object.assign( nextState,typeof partial === 'function' ? partial.call(inst,props,context) : partial ); } return nextState; } } 这一部分代码相对来说不算是很难, Object.assign( nextState,typeof partial === 'function' ? partial.call(inst,context) : partial ); 如果我们传入的是对象 this.setState({value: this.state.value + 1 }); this.setState({value: this.state.value + 1}) 我们现在已经知道,调用 function _performComponentUpdate( nextElement,unmaskedContext ) { var inst = this._instance; var hasComponentDidUpdate = Boolean(inst.componentDidUpdate); var prevProps; var prevState; var prevContext; if (hasComponentDidUpdate) { prevProps = inst.props; prevState = inst.state; prevContext = inst.context; } if (inst.componentWillUpdate) { inst.componentWillUpdate(nextProps,nextContext); } this._currentElement = nextElement; this._context = unmaskedContext; inst.props = nextProps; inst.state = nextState; inst.context = nextContext; this._updateRenderedComponent(transaction,unmaskedContext); if (hasComponentDidUpdate) { transaction.getReactMountReady().enqueue( inst.componentDidUpdate.bind(inst,prevProps,prevState,prevContext),inst ); } } 我们可以看到,这部分内容涉及到了几方面内容,首先在更新前调用了钩子函数 if (callback) { this.updater.enqueueCallback(this,callback); } call函数会被传递给 //代码有省略 var flushBatchedUpdates = function() { while (dirtyComponents.length) { if (dirtyComponents.length) { //从事务pool中获得事务实例 var transaction = ReactUpdatesFlushTransaction.getPooled(); transaction.perform(runBatchedUpdates,transaction); //释放实例 ReactUpdatesFlushTransaction.release(transaction); } //...... } }; 我们现在看看 var UPDATE_QUEUEING = { initialize: function() { this.callbackQueue.reset(); },close: function() { this.callbackQueue.notifyAll(); },}; 我们看到在事务的 //未按预期执行 _addValue() { this.setState({ value: this.state.value + 1 }) this.setState({ value: this.state.value + 1 }) } //按预期执行 _addValue() { setTimeout(()=>{ this.setState({ value: this.state.value + 1 }); this.setState({ value: this.state.value + 1 }); },0) } 这个问题,其实真的要追本溯源地去讲,是比较复杂的,我们简要介绍一下。在第一种情况下,如果打断点追踪你会发现,在第一次执行setState前,已经触发了一个 batchedUpdates,等到执行setState时已经处于一个较大的事务,因此两个setState都是会被批量更新的(相当于异步更新的过程,thi.state.value值并没有立即改变),执行setState只不过是将两者的 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |