深入研究React中setState源码

前端之家收集整理的这篇文章主要介绍了深入研究React中setState源码前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

React作为一门前端框架,虽然只是focus在MVVM中的View部分,但还是实现了View和model的绑定。修改数据的同时,可以实现View的刷新。这大大简化了我们的逻辑,只用关心数据流的变化,同时减少了代码量,使得后期维护也更加方便。这个特性则要归功于setState()方法。React中利用队列机制来管理state,避免了很多重复的View刷新。下面我们来从源码角度探寻下setState机制。

1 还是先声明一个组件,从最开始一步步来寻源;

函数的来源; //super其实就是下面这个函数 function ReactComponent(props,context,updater) { this.props = props; this.context = context; this.refs = emptyObject; // We initialize the default updater but the real one gets injected by the // 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中context updater到底是如何传递的

这里直接说结果,updater对象其实就是ReactUpdateQueue.js 中暴漏出的ReactUpdateQueue对象;

2 既然找到了setState之后执行的动作,我们在一步步深入进去

{this.state.count} ) } }

ReactComponent.prototype.setState = function (partialState,'setState');
}
};

ReactUpdateQueue.js

function enqueueUpdate(internalInstance) {
ReactUpdates.enqueueUpdate(internalInstance);
};
function getInternalInstanceReadyForUpdate(publicInstance,callerName) {
//在ReactCompositeComponent.js中有这样一行代码,这就是其来源;
// Store a reference from the instance back to the internal representation
//ReactInstanceMap.set(inst,this);
var internalInstance = ReactInstanceMap.get(publicInstance);
//返回的是在ReactCompositeComponent.js中construct函数返回的对象;ReactInstance实例对象并不是简单的new 我们写的组件的实例对象,而是经过instantiateReactComponent.js中ReactCompositeComponentWrapper函数包装的对象;详见 创建React组件方式以及源码分析.md
return internalInstance;
};
var ReactUpdateQueue = {
//。。。。省略其他代码
enqueueCallback: function (publicInstance,callerName) {
ReactUpdateQueue.validateCallback(callback,callerName);
var internalInstance = getInternalInstanceReadyForUpdate(publicInstance);
if (!internalInstance) {
return null;
}
//这里将callback放入组件实例的_pendingCallbacks数组中;
if (internalInstance._pendingCallbacks) {
internalInstance._pendingCallbacks.push(callback);
} else {
internalInstance._pendingCallbacks = [callback];
}
// TODO: The callback here is ignored when setState is called from
// componentWillMount. Either fix it or disallow doing so completely in
// favor of getInitialState. Alternatively,we can disallow
// componentWillMount during server-side rendering.
enqueueUpdate(internalInstance);
},enqueueSetState: function (publicInstance,partialState) {
var internalInstance = getInternalInstanceReadyForUpdate(publicInstance,'setState');
if (!internalInstance) {
return;
}
//这里,初始化queue变量,同时初始化 internalInstance._pendingStateQueue = [ ] ;
//对于 || 的短路运算还是要多梳理下
//queue数组(模拟队列)中存放着setState放进来的对象;
var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []);
//这里将partialState放入queue数组中,也就是internalInstance._pendingStateQueue 数组中,此时,每次setState的partialState,都放进了React组件实例对象上的_pendingStateQueue属性中,成为一个数组;
queue.push(partialState);

enqueueUpdate(internalInstance);
},};

module.exports = ReactUpdateQueue;

可以看到enqueueSetState enqueueCallback 最后都会执行enqueueUpdate;

ReactUpdates.js

注册给其赋值; var batchingStrategy = null; //这里的component参数是js中ReactCompositeComponentWrapper函数包装的后的React组件实例对象; function enqueueUpdate(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.close var FLUSH_BATCHED_UPDATES = { initialize: emptyFunction,close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates) };

var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES,RESET_BATCHED_UPDATES];

function ReactDefaultBatchingStrategyTransaction() {
this.reinitializeTransaction();
}

_assign(ReactDefaultBatchingStrategyTransaction.prototype,Transaction,{
getTransactionWrappers: function () {
return TRANSACTION_WRAPPERS;
}
});
//这个transition就是下面ReactDefaultBatchingStrategy对象中使用的transaction变量
var transaction = new ReactDefaultBatchingStrategyTransaction();
var ReactDefaultBatchingStrategy = {
isBatchingUpdates: false,/**

  • Call the provided function in a context within which calls to setState
  • and friends are batched such that components aren't updated unnecessarily.
    */
    batchedUpdates: function (callback,a,b,c,d,e) {
    var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;
    // 批处理最开始时,将isBatchingUpdates设为true,表明正在更新
    ReactDefaultBatchingStrategy.isBatchingUpdates = true;

// The code is written this way to avoid extra allocations
if (alreadyBatchingUpdates) {
return callback(a,e);
} else {
//transition在上面已经声明; // 以事务的方式处理updates,后面详细分析transaction
return transaction.perform(callback,null,e);
}
}
};

module.exports = ReactDefaultBatchingStrategy;

接下来我们看下React中的事物处理机制到底是如何运行的;

Transaction.js

函数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,e,f) { var errorThrown; var ret; try { this._isInTransaction = true; errorThrown = true; //var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES,RESET_BATCHED_UPDATES]; //1 这里会先执行所有的TRANSACTION_WRAPPERS中成员的initialize方法,上面声明的其都是emptyFunction this.initializeAll(0); //2 这里其实还是执行的 enqueueUpdate 函数 ret = method.call(scope,f); errorThrown = false; } finally { try { if (errorThrown) { // If `method` throws,prefer to show that stack trace over any thrown // by invoking `closeAll`. try { this.closeAll(0); } catch (err) {} } else { // Since `method` didn't throw,we don't want to silence the exception // here. //3 执行TRANSACTION_WRAPPERS对象中成员的所有close方法; this.closeAll(0); } } finally { this._isInTransaction = false; } } return ret; },initializeAll: function (startIndex) { var transactionWrappers = this.transactionWrappers; for (var i = startIndex; i < transactionWrappers.length; i++) { var wrapper = transactionWrappers[i]; try {
this.wrapperInitData[i] = OBSERVED_ERROR;
this.wrapperInitData[i] = wrapper.initialize ? wrapper.initialize.call(this) : null;

} finally {
if (this.wrapperInitData[i] === OBSERVED_ERROR) {

 try {
  this.initializeAll(i + 1);
 } catch (err) {}
}

}
}
},closeAll: function (startIndex) {
var transactionWrappers = this.transactionWrappers;
for (var i = startIndex; i < transactionWrappers.length; i++) {
var wrapper = transactionWrappers[i];
var initData = this.wrapperInitData[i];
var errorThrown;
try {

errorThrown = true;
if (initData !== OBSERVED_ERROR && wrapper.close) {
 wrapper.close.call(this,initData);
}
errorThrown = false;

} finally {
if (errorThrown) {

 try {
  this.closeAll(i + 1);
 } catch (e) {}
}

}
}
this.wrapperInitData.length = 0;
}
};

module.exports = TransactionImpl

//3 执行TRANSACTION_WRAPPERS对象中成员的所有close方法
var FLUSH_BATCHED_UPDATES = {
initialize: emptyFunction,close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates)
};

接着会执行ReactUpdates.js中的flushBatchedUpdates方法

ReactUpdates.js中

while (dirtyComponents.length || asapEnqueued) {
if (dirtyComponents.length) {
var transaction = ReactUpdatesFlushTransaction.getPooled();
//这里执行runBatchedUpdates函数
transaction.perform(runBatchedUpdates,transaction);
ReactUpdatesFlushTransaction.release(transaction);
}

if (asapEnqueued) {
asapEnqueued = false;
var queue = asapCallbackQueue;
asapCallbackQueue = CallbackQueue.getPooled();
queue.notifyAll();
CallbackQueue.release(queue);
}
}
};
function runBatchedUpdates(transaction) {
var len = transaction.dirtyComponentsLength;

dirtyComponents.sort(mountOrderComparator);

updateBatchNumber++;

for (var i = 0; i < len; i++) {

var component = dirtyComponents[i];
var callbacks = component._pendingCallbacks;
component._pendingCallbacks = null;

var markerName;
if (ReactFeatureFlags.logTopLevelRenders) {
var namedComponent = component;
// Duck type TopLevelWrapper. This is probably always true.
if (component._currentElement.type.isReactTopLevelWrapper) {
namedComponent = component._renderedComponent;
}
markerName = 'React update: ' + namedComponent.getName();
console.time(markerName);
}
//这里才是真正的开始更新组件
ReactReconciler.performUpdateIfNecessary(component,transaction.reconcileTransaction,updateBatchNumber);

if (markerName) {
console.timeEnd(markerName);
}

if (callbacks) {
for (var j = 0; j < callbacks.length; j++) {
transaction.callbackQueue.enqueue(callbacks[j],component.getPublicInstance());
}
}
}
}

ReactReconciler.js中

ReactCompositeComponent.js

调用到updateComponent,从而刷新View ReactReconciler.receiveComponent(this,this._pendingElement,this._context); } else if (this._pendingStateQueue !== null || this._pendingForceUpdate) { // 执行updateComponent,从而刷新View。 this.updateComponent(transaction,this._currentElement,this._context,this._context); } else { this._updateBatchNumber = null; } },//执行更新React组件的props. state。context函数

updateComponent: function (transaction,prevParentElement,nextParentElement,prevUnmaskedContext,nextUnmaskedContext) {
var inst = this._instance;
var willReceive = false;
var nextContext;
// Determine if the context has changed or not
if (this._context === nextUnmaskedContext) {
nextContext = inst.context;
} else {
nextContext = this._processContext(nextUnmaskedContext);
willReceive = true;
}

var prevProps = prevParentElement.props;
var nextProps = nextParentElement.props;

// Not a simple state update but a props update
if (prevParentElement !== nextParentElement) {
willReceive = true;
}

// An update here will schedule an update but immediately set
// _pendingStateQueue which will ensure that any state updates gets
// immediately reconciled instead of waiting for the next batch.
if (willReceive && inst.componentWillReceiveProps) {
if (process.env.NODE_ENV !== 'production') {
measureLifeCyclePerf(function () {
return inst.componentWillReceiveProps(nextProps,nextContext);
},this._debugID,'componentWillReceiveProps');
} else {
inst.componentWillReceiveProps(nextProps,nextContext);
}
}
//这里可以知道为什么setState可以接受函数,主要就是_processPendingState函数
//这里仅仅是将每次setState放入到_pendingStateQueue队列中的值,合并到nextState,并没有真正的更新state的值;真正更新组件的state的值是在下面;
var nextState = this._processPendingState(nextProps,nextContext);
var shouldUpdate = true;

if (!this._pendingForceUpdate) {
if (inst.shouldComponentUpdate) {
if (process.env.NODE_ENV !== 'production') {
shouldUpdate = measureLifeCyclePerf(function () {
return inst.shouldComponentUpdate(nextProps,nextState,nextContext);
},'shouldComponentUpdate');
} else {
shouldUpdate = inst.shouldComponentUpdate(nextProps,nextContext);
}
} else {
if (this._compositeType === CompositeTypes.PureClass) {
shouldUpdate = !shallowEqual(prevProps,nextProps) || !shallowEqual(inst.state,nextState);
}
}
}

this._updateBatchNumber = null;
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.
//诺:在这里更新组件的state. props 等值;
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 = _assign({},replace ? queue[0] : inst.state);
for (var i = replace ? 1 : 0; i < queue.length; i++) {
var partial = queue[i];
//如果是setState的参数是一个函数,那么该函数接受三个参数,分别是state props context
_assign(nextState,typeof partial === 'function' ? partial.call(inst,props,context) : partial);
}

return nextState;
},

this.state的更新会在_processPendingState执行完执行。所以两次setState取到的都是this.state.count最初的值0,这就解释了之前的现象。其实,这也是React为了解决这种前后state依赖但是state又没及时更新的一种方案,因此在使用时大家要根据实际情况来判断该用哪种方式传参。来看个小例子直观感受下

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流程还是很复杂的,设计也很精巧,避免了重复无谓的刷新组件。它的主要流程如下

  1. enqueueSetState将state放入队列中,并调用enqueueUpdate处理要更新的Component
  2. 如果组件当前正处于update事务中,则先将Component存入dirtyComponent中。否则调用batchedUpdates处理。
  3. batchedUpdates发起一次transaction.perform()事务
  4. 开始执行事务初始化,运行,结束三个阶段
    1. 初始化:事务初始化阶段没有注册方法,故无方法要执行
    2. 运行:执行setSate时传入的callback方法,一般不会传callback参数
    3. 结束:更新isBatchingUpdates为false,并执行FLUSH_BATCHED_UPDATES这个wrapper中的close方法
  5. FLUSH_BATCHED_UPDATES在close阶段,会循环遍历所有的dirtyComponents,调用updateComponent刷新组件,并执行它的pendingCallbacks,也就是setState中设置的callback。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程之家。

猜你在找的JavaScript相关文章