以代码注释的方式整理setState的执行流程
用class 的方式创建React组件,需要继承React.Component,而setState()方法就绑定在他的原型上面
- 发现setState()上有两个参数
- 第一个参数是需要修改的state对象,或者是函数(preState:更新前的sate,props:当前的属性)
- 第二个参数是修改完state执行的回调函数
ReactComponent.prototype.isReactComponent = {};
ReactComponent.prototype.setState = function(partialState, callback) {
//这里的this.updater就是ReactUpdateQueue,--- this是组件的实例
this.updater.enqueueSetState(this, partialState);
if (callback) {
this.updater.enqueueCallback(this, callback, 'setState');
}
};
复制代码
- 调用了this.updater.enqueueSetState(this, partialState);
- 主要是初始化更新队列,并将需要修改的state加入到更新队列中
enqueueSetState: function(publicInstance, partialState) {
//获取当前实例
//实例中有两个非常重要的属性
//_pendingStateQueue(待更新队列) 与 _pendingCallbacks(更新回调队列)
var internalInstance = getInternalInstanceReadyForUpdate( publicInstance, 'setState', );
if (!internalInstance) {
return;
}
//初始化待更新队列
var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []);
queue.push(partialState);
enqueueUpdate(internalInstance);
},
复制代码
- 调用enqueueUpdate(internalInstance)时调用 ReactUpdates.enqueueUpdate(internalInstance);
function enqueueUpdate(internalInstance) {
ReactUpdates.enqueueUpdate(internalInstance);
}
复制代码
- 这个方法很关键
- 这个方法的主要内容就是当 isBatchingUpdates = false 时,开启批量更新模式
- 否则就会将组件对象加入到批量更新组件的数组中
-
- 需要注意的是 isBatchingUpdates == true 就直接将组件push数组.记住这个判断
function enqueueUpdate(component) {
//如果不处于批量更新模式
if (!batchingStrategy.isBatchingUpdates) {
batchingStrategy.batchedUpdates(enqueueUpdate, component);
return;
}
//如果处于批量更新模式,则将该组件保存在dirtyComponents数组中
dirtyComponents.push(component);
}
复制代码
- 当没有开启批量更新模式的时候调用 batchingStrategy.batchedUpdates(enqueueUpdate, component);
- batchedUpdates() 其实是ReactDefaultBatchingStrategy对象的一个函数
- 不管开没开起批量更新模式,都会先保存之前的状态,然后开启更新模式
- 如果没开启更新模式,就使用事物的方式调用回调函数
/**
* 调用batchingStrategy.batchedUpdates(enqueueUpdate, component);
* ReactDefaultBatchingStrategy实际上是一个批量更新策略
*/
var ReactDefaultBatchingStrategy = {
isBatchingUpdates: false,
batchedUpdates: function(callback, a, b, c, d, e) {
//第一次的时候为false
var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;
//然后改成true,体会下什么时候在改成true
ReactDefaultBatchingStrategy.isBatchingUpdates = true;
if (alreadyBatchingUpdates) {
return callback(a, b, c, d, e);
} else {
return transaction.perform(callback, null, a, b, c, d, e);
}
},
};
复制代码
- transaction.perform()
- 这里涉及到了事物的概念
- Transaction会给每一个需要执行的方法包装一个wrapper,而这个wrapper内有两个方法 initialize 与 close 当要执行目标方法前要先执行 initialize() 然后才是目标方法 之后再执行close 而这里 initialize() 是空函数
- 简而言之就是wrapper(initialize,perform,close)顺序执行,而initialize还是个空函数,所以执行完perform,再执行close
- RESET_BATCHED_UPDATES,FLUSH_BATCHED_UPDATES 定义了两个wrapper
- RESET_BATCHED_UPDATES 负责在close阶段重置 ReactDefaultBatchingStrategy.isBatchingUpdates = false;
- FLUSH_BATCHED_UPDATES 负责在close阶段 执行ReactUpdates.flushBatchedUpdates.bind(ReactUpdates);
- 然后把这两个放到一个数组中,在ReactDefaultBatchingStrategyTransaction的原型上绑定getTransactionWrappers用于返回wrapper的数组;
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();
}
/**
* 把wrapper绑定到ReactDefaultBatchingStrategyTransaction的原型上
*/
Object.assign(ReactDefaultBatchingStrategyTransaction.prototype, Transaction, {
getTransactionWrappers: function() {
return TRANSACTION_WRAPPERS;
},
});
复制代码
- FLUSH_BATCHED_UPDATES.close=>ReactUpdates.flushBatchedUpdates.bind(ReactUpdates)
- 这个方法再迭代更新组件数组,然后又以事物的方式调用了runBatchedUpdates()方法
- 执行完回来释放实例
var flushBatchedUpdates = function() {
while (dirtyComponents.length || asapEnqueued) {
if (dirtyComponents.length) {
var transaction = ReactUpdatesFlushTransaction.getPooled();
//又以事物的形式调用了 runBatchedUpdates()
transaction.perform(runBatchedUpdates, null, transaction);
//释放实例
ReactUpdatesFlushTransaction.release(transaction);
}
if (asapEnqueued) {
asapEnqueued = false;
var queue = asapCallbackQueue;
asapCallbackQueue = CallbackQueue.getPooled();
queue.notifyAll();
CallbackQueue.release(queue);
}
}
};
复制代码
- 根据名称就知道这是个执行批量更新的操作
- 这个方法主要就是调用了组件的performUpdateIfNecessary()
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;
if (component._currentElement.type.isReactTopLevelWrapper) {
namedComponent = component._renderedComponent;
}
markerName = 'React update: ' + namedComponent.getName();
console.time(markerName);
}
//经过一些列处理 其实就是调用了component.performUpdateIfNecessary,也就是自定义组件ReactCompositeComponent.performUpdateIfNecessary()
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());
}
}
}
}
复制代码
- 发现其实是调用了组件实例的performUpdateIfNecessary()
/**
* 然后关键 ReactReconciler.performUpdateIfNecessary( component, transaction.reconcileTransaction, updateBatchNumber, );
*/
performUpdateIfNecessary: function(internalInstance,transaction,updateBatchNumber,) {
if (internalInstance._updateBatchNumber !== updateBatchNumber) {
return;
}
internalInstance.performUpdateIfNecessary(transaction);
},
复制代码
- 然后又回到了ReactCompositeComponent自定义组件
/**
* internalInstance.performUpdateIfNecessary(transaction);
* 调用了实例的performUpdateIfNecessary方法,这个实例就是自定义组件的实例
*/
// 如果存在 _pendingElement、_pendingStateQueue和_pendingForceUpdate,则更新组件
performUpdateIfNecessary: function(transaction) {
if (this._pendingElement != null) {
ReactReconciler.receiveComponent(this, this._pendingElement,transaction,this._context,);
} else if (this._pendingStateQueue !== null || this._pendingForceUpdate) {
this.updateComponent(transaction,this._currentElement,this._currentElement,this._context,this._context,);
} else {
this._updateBatchNumber = null;
}
},
复制代码
- 跳到了非常熟悉的updateComponent方法
updateComponent: function(transaction,prevParentElement,nextParentElement,prevUnmaskedContext,nextUnmaskedContext,) {
var inst = this._instance;
var willReceive = false;
var nextContext;
// 上下文是否改变
if (this._context === nextUnmaskedContext) {
nextContext = inst.context;
} else {
nextContext = this._processContext(nextUnmaskedContext);
willReceive = true;
}
var prevProps = prevParentElement.props;
var nextProps = nextParentElement.props;
// 新旧属性不同
if (prevParentElement !== nextParentElement) {
willReceive = true;
}
//新旧属性不同,并且存在componentWillReceiveProps,就执行componentWillReceiveProps()
if (willReceive && inst.componentWillReceiveProps) {
inst.componentWillReceiveProps(nextProps, nextContext);
}
//将新的state合并到更新队列中,此时的nextState是最新的state
var nextState = this._processPendingState(nextProps, nextContext);
var shouldUpdate = true;
//根据更新队列和shouldComponentUpdate的状态来判断是否需要更新组件
if (!this._pendingForceUpdate) {
if (inst.shouldComponentUpdate) {
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;
//即将更新this.props,this.state,和this.context
this._performComponentUpdate(nextParentElement,nextProps, nextState,nextContext,transaction,nextUnmaskedContext,);
} else {
// 如果确定组件不更新,仍然要这是props和state
this._currentElement = nextParentElement;
this._context = nextUnmaskedContext;
inst.props = nextProps;
inst.state = nextState;
inst.context = nextContext;
}
},
/**
* 调用了this._processPendingState(nextProps, nextContext)
*/
_processPendingState: function(props, context) {
var inst = this._instance;
var queue = this._pendingStateQueue;
var replace = this._pendingReplaceState;
this._pendingReplaceState = false;
this._pendingStateQueue = null;
//如果队列为null,返回原state
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];
//这里就是setState的第一个参数的另外一种情况,可以传入函数
//这里用函数的形式,合并到了nextState上,就获取了最新的state值
Object.assign(
nextState,
typeof partial === 'function'
? partial.call(inst, nextState, props, context)
: partial,
);
}
//返回了最新的state的值
return nextState;
},
复制代码
- 在执行完componentWillUpdate 之后才能获取最新的state值
/**
* 当组件需要更新时,调用
*/
_performComponentUpdate: function(nextElement,nextProps,nextState,nextContext,transaction,unmaskedContext) {
var inst = this._instance;
var hasComponentDidUpdate = Boolean(inst.componentDidUpdate);
var prevProps;
var prevState;
var prevContext;
//如果存在componentDidUpdate,则将当前的props,state和context保存一份
if (hasComponentDidUpdate) {
prevProps = inst.props;
prevState = inst.state;
prevContext = inst.context;
}
//执行componentWillUpdate
if (inst.componentWillUpdate) {
inst.componentWillUpdate(nextProps, nextState, nextContext);
}
//更新this.props,this.state,this.context
//这个时候才开始给实例的props和state,context赋值
this._currentElement = nextElement;
this._context = unmaskedContext;
inst.props = nextProps;
inst.state = nextState;
inst.context = nextContext;
//渲染组件
this._updateRenderedComponent(transaction, unmaskedContext);
//当组件完成更新后,如果存在ComponentDidUpdate,则调用
if (hasComponentDidUpdate) {
transaction.getReactMountReady().enqueue(inst.componentDidUpdate.bind(inst,prevProps, prevState,prevContext,),inst,);
}
},
/**
* 这里runBatchedUpdates的事物才执行完毕,也就是说在componentDidUpdate()之后调用事物的close,
*/
复制代码
setState的第二个参数问题
ReactComponent.prototype.setState = function(partialState, callback) {
//这里的this.updater就是ReactUpdateQueue,--- this是组件的实例
this.updater.enqueueSetState(this, partialState);
if (callback) {
this.updater.enqueueCallback(this, callback, 'setState');
}
};
/**
* 跟enqueueSetState 类似
* 执行这个方法最主要的就是将回调函数绑定到了internalInstance._pendingCallbacks上
*/
enqueueCallback: function(publicInstance, callback, callerName) {
var internalInstance = getInternalInstanceReadyForUpdate(publicInstance);
if (!internalInstance) {
return null;
}
//就是初始化_pendingCallbacks
if (internalInstance._pendingCallbacks) {
internalInstance._pendingCallbacks.push(callback);
} else {
internalInstance._pendingCallbacks = [callback];
}
enqueueUpdate(internalInstance);
},
/**
* 最后又跳到了这里,就不重要了
*/
function enqueueUpdate(internalInstance) {
ReactUpdates.enqueueUpdate(internalInstance);
}
/**
* 回调函数是在setState执行完之后再执行的
*/
/**
* 该方法用于迭代dirtyComponents
*/
var flushBatchedUpdates = function() {
while (dirtyComponents.length || asapEnqueued) {
if (dirtyComponents.length) {
var transaction = ReactUpdatesFlushTransaction.getPooled();
//又以事物的形式调用了 runBatchedUpdates()
transaction.perform(runBatchedUpdates, null, transaction);
//释放实例
ReactUpdatesFlushTransaction.release(transaction);
}
if (asapEnqueued) {
asapEnqueued = false;
var queue = asapCallbackQueue;
asapCallbackQueue = CallbackQueue.getPooled();
queue.notifyAll();
CallbackQueue.release(queue);
}
}
};
/**
* 以下是ReactUpdatesFlushTransaction定义
* 在close的时候发现调用了this.callbackQueue.notifyAll();
* 而notifyAll方法是取出回调函数开始执行
*/
var UPDATE_QUEUEING = {
initialize: function() {
this.callbackQueue.reset();
},
close: function() {
this.callbackQueue.notifyAll();
},
};
var TRANSACTION_WRAPPERS = [NESTED_UPDATES, UPDATE_QUEUEING];
function ReactUpdatesFlushTransaction() {
this.reinitializeTransaction();
this.dirtyComponentsLength = null;
this.callbackQueue = CallbackQueue.getPooled();
this.reconcileTransaction = ReactUpdates.ReactReconcileTransaction.getPooled(true,);
}
/**
* 执行回调函数
*/
notifyAll() {
var callbacks = this._callbacks;
var contexts = this._contexts;
var arg = this._arg;
if (callbacks && contexts) {
this._callbacks = null;
this._contexts = null;
for (var i = 0; i < callbacks.length; i++) {
callbacks[i].call(contexts[i], arg);
}
callbacks.length = 0;
contexts.length = 0;
}
}
复制代码
setState的异步处理
这个可以追踪源码理解,经过上面的流程那么这个就不难解释了
- 直接调用this.setState()
- 因为在整个生命周期中就是一个事物操作,所以标识位isBatchingUpdates = true,所以流程到了enqueueUpdate()时,实例对象都会加入到dirtyComponents 数组中
- 接着执行componentWillMount()
- 将所有的setState操作进行合并(相同的操作合并成一个)
- 渲染
- 执行componentDidMount()
- 事物结束,调用close方法->调用runBatchUpdate()
- 继续走更新流程
- 使用setTimeout()方法调用
- setTimeout()没在事物中 isBatchingUpdates = false 所以就直接走直接更新操作