深入研究React中setState源码
React作为一门前端框架,虽然只是focus在MVVM中的View部分,但还是实现了View和model的绑定。修改数据的同时,可以实现View的刷新。这大大简化了我们的逻辑,只用关心数据流的变化,同时减少了代码量,使得后期维护也更加方便。这个特性则要归功于setState()方法。React中利用队列机制来管理state,避免了很多重复的View刷新。下面我们来从源码角度探寻下setState机制。
1还是先声明一个组件,从最开始一步步来寻源;
classAppextendsComponent{ //只在组件重新加载的时候执行一次 constructor(props){ super(props); //.. } //othermethods } //ReactBaseClasses.js中如下:这里就是setState函数的来源; //super其实就是下面这个函数 functionReactComponent(props,context,updater){ this.props=props; this.context=context; this.refs=emptyObject; //Weinitializethedefaultupdaterbuttherealonegetsinjectedbythe //renderer. this.updater=updater||ReactNoopUpdateQueue; } ReactComponent.prototype.setState=function(partialState,callback){ this.updater.enqueueSetState(this,partialState); if(callback){ this.updater.enqueueCallback(this,callback,'setState'); } };
所以主要来看是否传入了updater参数,也就是说何时进行new组件;具体的updater参数是怎么传递进来的,以及是那个对象,参见
react源码分析系列文章下面的react中contextupdater到底是如何传递的
这里直接说结果,updater对象其实就是ReactUpdateQueue.js中暴漏出的ReactUpdateQueue对象;
2既然找到了setState之后执行的动作,我们在一步步深入进去
classRootextendsReact.Component{ constructor(props){ super(props); this.state={ count:0 }; } componentDidMount(){ letme=this; me.setState({ count:me.state.count+1 }); console.log(me.state.count);//打印出0 me.setState({ count:me.state.count+1 }); console.log(me.state.count);//打印出0 setTimeout(function(){ me.setState({ count:me.state.count+1 }); console.log(me.state.count);//打印出2 },0); setTimeout(function(){ me.setState({ count:me.state.count+1 }); console.log(me.state.count);//打印出3 },0); } render(){ return({this.state.count}
) } } ReactComponent.prototype.setState=function(partialState,callback){ this.updater.enqueueSetState(this,partialState); if(callback){ this.updater.enqueueCallback(this,callback,'setState'); } };
ReactUpdateQueue.js
varReactUpdates=require('./ReactUpdates'); functionenqueueUpdate(internalInstance){ ReactUpdates.enqueueUpdate(internalInstance); }; functiongetInternalInstanceReadyForUpdate(publicInstance,callerName){ //在ReactCompositeComponent.js中有这样一行代码,这就是其来源; //Storeareferencefromtheinstancebacktotheinternalrepresentation //ReactInstanceMap.set(inst,this); varinternalInstance=ReactInstanceMap.get(publicInstance); //返回的是在ReactCompositeComponent.js中construct函数返回的对象;ReactInstance实例对象并不是简单的new我们写的组件的实例对象,而是经过instantiateReactComponent.js中ReactCompositeComponentWrapper函数包装的对象;详见创建React组件方式以及源码分析.md returninternalInstance; }; varReactUpdateQueue={ //。。。。省略其他代码 enqueueCallback:function(publicInstance,callback,callerName){ ReactUpdateQueue.validateCallback(callback,callerName); varinternalInstance=getInternalInstanceReadyForUpdate(publicInstance); if(!internalInstance){ returnnull; } //这里将callback放入组件实例的_pendingCallbacks数组中; if(internalInstance._pendingCallbacks){ internalInstance._pendingCallbacks.push(callback); }else{ internalInstance._pendingCallbacks=[callback]; } //TODO:ThecallbackhereisignoredwhensetStateiscalledfrom //componentWillMount.Eitherfixitordisallowdoingsocompletelyin //favorofgetInitialState.Alternatively,wecandisallow //componentWillMountduringserver-siderendering. enqueueUpdate(internalInstance); }, enqueueSetState:function(publicInstance,partialState){ varinternalInstance=getInternalInstanceReadyForUpdate(publicInstance,'setState'); if(!internalInstance){ return; } //这里,初始化queue变量,同时初始化internalInstance._pendingStateQueue=[]; //对于||的短路运算还是要多梳理下 //queue数组(模拟队列)中存放着setState放进来的对象; varqueue=internalInstance._pendingStateQueue||(internalInstance._pendingStateQueue=[]); //这里将partialState放入queue数组中,也就是internalInstance._pendingStateQueue数组中,此时,每次setState的partialState,都放进了React组件实例对象上的_pendingStateQueue属性中,成为一个数组; queue.push(partialState); enqueueUpdate(internalInstance); }, }; module.exports=ReactUpdateQueue;
可以看到enqueueSetStateenqueueCallback最后都会执行enqueueUpdate;
functionenqueueUpdate(internalInstance){ ReactUpdates.enqueueUpdate(internalInstance); }
ReactUpdates.js
vardirtyComponents=[]; varupdateBatchNumber=0; varasapCallbackQueue=CallbackQueue.getPooled(); varasapEnqueued=false; //这里声明batchingStrategy为null,后期通过注册给其赋值; varbatchingStrategy=null; //这里的component参数是js中ReactCompositeComponentWrapper函数包装的后的React组件实例对象; functionenqueueUpdate(component){ ensureInjected(); //第一次执行setState的时候,可以进入if语句,遇到里面的return语句,终止执行 //如果不是正处于创建或更新组件阶段,则处理update事务 if(!batchingStrategy.isBatchingUpdates){ //batchedUpdates就是ReactDefaultBatchingStrategy.js中声明的 batchingStrategy.batchedUpdates(enqueueUpdate,component); return; } //第二次执行setState的时候,进入不了if语句,将组件放入dirtyComponents //如果正在创建或更新组件,则暂且先不处理update,只是将组件放在dirtyComponents数组中 dirtyComponents.push(component); if(component._updateBatchNumber==null){ component._updateBatchNumber=updateBatchNumber+1; } }; //enqueueUpdate包含了React避免重复render的逻辑。mountComponent和updateComponent方法在执行的最开始,会调用到batchedUpdates进行批处理更新,此时会将isBatchingUpdates设置为true,也就是将状态标记为现在正处于更新阶段了。之后React以事务的方式处理组件update,事务处理完后会调用wrapper.close(),而TRANSACTION_WRAPPERS中包含了RESET_BATCHED_UPDATES这个wrapper,故最终会调用RESET_BATCHED_UPDATES.close(),它最终会将isBatchingUpdates设置为false。
ReactDefaultBatchingStrategy.js
//RESET_BATCHED_UPDATES用来管理isBatchingUpdates的状态 varRESET_BATCHED_UPDATES={ initialize:emptyFunction, close:function(){ //事务批更新处理结束时,将isBatchingUpdates设为了false ReactDefaultBatchingStrategy.isBatchingUpdates=false; } }; //FLUSH_BATCHED_UPDATES会在一个transaction的close阶段运行runBatchedUpdates,从而执行update。 //因为close的执行顺序是FLUSH_BATCHED_UPDATES.close==>然后RESET_BATCHED_UPDATES.close varFLUSH_BATCHED_UPDATES={ initialize:emptyFunction, close:ReactUpdates.flushBatchedUpdates.bind(ReactUpdates) }; varTRANSACTION_WRAPPERS=[FLUSH_BATCHED_UPDATES,RESET_BATCHED_UPDATES]; functionReactDefaultBatchingStrategyTransaction(){ this.reinitializeTransaction(); } _assign(ReactDefaultBatchingStrategyTransaction.prototype,Transaction,{ getTransactionWrappers:function(){ returnTRANSACTION_WRAPPERS; } }); //这个transition就是下面ReactDefaultBatchingStrategy对象中使用的transaction变量 vartransaction=newReactDefaultBatchingStrategyTransaction(); varReactDefaultBatchingStrategy={ isBatchingUpdates:false, /** *Calltheprovidedfunctioninacontextwithinwhichcallsto`setState` *andfriendsarebatchedsuchthatcomponentsaren'tupdatedunnecessarily. */ batchedUpdates:function(callback,a,b,c,d,e){ varalreadyBatchingUpdates=ReactDefaultBatchingStrategy.isBatchingUpdates; //批处理最开始时,将isBatchingUpdates设为true,表明正在更新 ReactDefaultBatchingStrategy.isBatchingUpdates=true; //Thecodeiswrittenthiswaytoavoidextraallocations if(alreadyBatchingUpdates){ returncallback(a,b,c,d,e); }else{ //transition在上面已经声明;//以事务的方式处理updates,后面详细分析transaction returntransaction.perform(callback,null,a,b,c,d,e); } } }; module.exports=ReactDefaultBatchingStrategy;
接下来我们看下React中的事物处理机制到底是如何运行的;
Transaction.js
var_prodInvariant=require('./reactProdInvariant'); varinvariant=require('fbjs/lib/invariant'); varOBSERVED_ERROR={}; varTransactionImpl={ reinitializeTransaction:function(){ //getTransactionWrappers这个函数ReactDefaultBatchingStrategy.js中声明的,上面有;返回一个数组; this.transactionWrappers=this.getTransactionWrappers(); if(this.wrapperInitData){ this.wrapperInitData.length=0; }else{ this.wrapperInitData=[]; } this._isInTransaction=false; }, _isInTransaction:false, getTransactionWrappers:null, isInTransaction:function(){ return!!this._isInTransaction; }, perform:function(method,scope,a,b,c,d,e,f){ varerrorThrown; varret; try{ this._isInTransaction=true; errorThrown=true; //varTRANSACTION_WRAPPERS=[FLUSH_BATCHED_UPDATES,RESET_BATCHED_UPDATES]; //1这里会先执行所有的TRANSACTION_WRAPPERS中成员的initialize方法,上面声明的其都是emptyFunction this.initializeAll(0); //2这里其实还是执行的enqueueUpdate函数 ret=method.call(scope,a,b,c,d,e,f); errorThrown=false; }finally{ try{ if(errorThrown){ //If`method`throws,prefertoshowthatstacktraceoveranythrown //byinvoking`closeAll`. try{ this.closeAll(0); }catch(err){} }else{ //Since`method`didn'tthrow,wedon'twanttosilencetheexception //here. //3执行TRANSACTION_WRAPPERS对象中成员的所有close方法; this.closeAll(0); } }finally{ this._isInTransaction=false; } } returnret; }, initializeAll:function(startIndex){ vartransactionWrappers=this.transactionWrappers; for(vari=startIndex;i接着会执行ReactUpdates.js中的flushBatchedUpdates方法
ReactUpdates.js中
varflushBatchedUpdates=function(){ while(dirtyComponents.length||asapEnqueued){ if(dirtyComponents.length){ vartransaction=ReactUpdatesFlushTransaction.getPooled(); //这里执行runBatchedUpdates函数; transaction.perform(runBatchedUpdates,null,transaction); ReactUpdatesFlushTransaction.release(transaction); } if(asapEnqueued){ asapEnqueued=false; varqueue=asapCallbackQueue; asapCallbackQueue=CallbackQueue.getPooled(); queue.notifyAll(); CallbackQueue.release(queue); } } }; functionrunBatchedUpdates(transaction){ varlen=transaction.dirtyComponentsLength; dirtyComponents.sort(mountOrderComparator); updateBatchNumber++; for(vari=0;iReactReconciler.js中
performUpdateIfNecessary:function(internalInstance,transaction,updateBatchNumber){ if(internalInstance._updateBatchNumber!==updateBatchNumber){ //Thecomponent'senqueuedbatchnumbershouldalwaysbethecurrent //batchorthefollowingone. return; } //这里执行React组件实例对象的更新;internalInstance上的performUpdateIfNecessary在ReactCompositeComponent.js中的; internalInstance.performUpdateIfNecessary(transaction); if(process.env.NODE_ENV!=='production'){ if(internalInstance._debugID!==0){ ReactInstrumentation.debugTool.onUpdateComponent(internalInstance._debugID); } } }ReactCompositeComponent.js
performUpdateIfNecessary:function(transaction){ if(this._pendingElement!=null){ //receiveComponent会最终调用到updateComponent,从而刷新View ReactReconciler.receiveComponent(this,this._pendingElement,transaction,this._context); }elseif(this._pendingStateQueue!==null||this._pendingForceUpdate){ //执行updateComponent,从而刷新View。 this.updateComponent(transaction,this._currentElement,this._currentElement,this._context,this._context); }else{ this._updateBatchNumber=null; } }, //执行更新React组件的props.state。context函数 updateComponent:function(transaction,prevParentElement,nextParentElement,prevUnmaskedContext,nextUnmaskedContext){ varinst=this._instance; varwillReceive=false; varnextContext; //Determineifthecontexthaschangedornot if(this._context===nextUnmaskedContext){ nextContext=inst.context; }else{ nextContext=this._processContext(nextUnmaskedContext); willReceive=true; } varprevProps=prevParentElement.props; varnextProps=nextParentElement.props; //Notasimplestateupdatebutapropsupdate if(prevParentElement!==nextParentElement){ willReceive=true; } //Anupdateherewillscheduleanupdatebutimmediatelyset //_pendingStateQueuewhichwillensurethatanystateupdatesgets //immediatelyreconciledinsteadofwaitingforthenextbatch. if(willReceive&&inst.componentWillReceiveProps){ if(process.env.NODE_ENV!=='production'){ measureLifeCyclePerf(function(){ returninst.componentWillReceiveProps(nextProps,nextContext); },this._debugID,'componentWillReceiveProps'); }else{ inst.componentWillReceiveProps(nextProps,nextContext); } } //这里可以知道为什么setState可以接受函数,主要就是_processPendingState函数; //这里仅仅是将每次setState放入到_pendingStateQueue队列中的值,合并到nextState,并没有真正的更新state的值;真正更新组件的state的值是在下面; varnextState=this._processPendingState(nextProps,nextContext); varshouldUpdate=true; if(!this._pendingForceUpdate){ if(inst.shouldComponentUpdate){ if(process.env.NODE_ENV!=='production'){ shouldUpdate=measureLifeCyclePerf(function(){ returninst.shouldComponentUpdate(nextProps,nextState,nextContext); },this._debugID,'shouldComponentUpdate'); }else{ shouldUpdate=inst.shouldComponentUpdate(nextProps,nextState,nextContext); } }else{ if(this._compositeType===CompositeTypes.PureClass){ shouldUpdate=!shallowEqual(prevProps,nextProps)||!shallowEqual(inst.state,nextState); } } } this._updateBatchNumber=null; if(shouldUpdate){ this._pendingForceUpdate=false; //Willset`this.props`,`this.state`and`this.context`. this._performComponentUpdate(nextParentElement,nextProps,nextState,nextContext,transaction,nextUnmaskedContext); }else{ //Ifit'sdeterminedthatacomponentshouldnotupdate,westillwant //tosetpropsandstatebutweshortcuttherestoftheupdate. //诺:在这里更新组件的state.props等值; this._currentElement=nextParentElement; this._context=nextUnmaskedContext; inst.props=nextProps; inst.state=nextState; inst.context=nextContext; } }, _processPendingState:function(props,context){ varinst=this._instance; varqueue=this._pendingStateQueue; varreplace=this._pendingReplaceState; this._pendingReplaceState=false; this._pendingStateQueue=null; if(!queue){ returninst.state; } if(replace&&queue.length===1){ returnqueue[0]; } varnextState=_assign({},replace?queue[0]:inst.state); for(vari=replace?1:0;ithis.state的更新会在_processPendingState执行完执行。所以两次setState取到的都是this.state.count最初的值0,这就解释了之前的现象。其实,这也是React为了解决这种前后state依赖但是state又没及时更新的一种方案,因此在使用时大家要根据实际情况来判断该用哪种方式传参。来看个小例子直观感受下
handleClickOnLikeButton(){ this.setState({count:0})//=>this.state.count还是undefined this.setState({count:this.state.count+1})//=>undefined+1=NaN this.setState({count:this.state.count+2})//=>NaN+2=NaN } //....VS.... handleClickOnLikeButton(){ this.setState((prevState)=>{ return{count:0} }) this.setState((prevState)=>{ return{count:prevState.count+1}//上一个setState的返回是count为0,当前返回1 }) this.setState((prevState)=>{ return{count:prevState.count+2}//上一个setState的返回是count为1,当前返回3 }) //最后的结果是this.state.count为3 } ...setState流程还是很复杂的,设计也很精巧,避免了重复无谓的刷新组件。它的主要流程如下
- enqueueSetState将state放入队列中,并调用enqueueUpdate处理要更新的Component
- 如果组件当前正处于update事务中,则先将Component存入dirtyComponent中。否则调用batchedUpdates处理。
- batchedUpdates发起一次transaction.perform()事务
- 开始执行事务初始化,运行,结束三个阶段
- 初始化:事务初始化阶段没有注册方法,故无方法要执行
- 运行:执行setSate时传入的callback方法,一般不会传callback参数
- 结束:更新isBatchingUpdates为false,并执行FLUSH_BATCHED_UPDATES这个wrapper中的close方法
- FLUSH_BATCHED_UPDATES在close阶段,会循环遍历所有的dirtyComponents,调用updateComponent刷新组件,并执行它的pendingCallbacks,也就是setState中设置的callback。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。