深入研究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;i
ReactReconciler.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;i
this.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。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。