目录setState同步 异步更新setState()被调用之后,源码执行栈1. setState()2. enqueueSetState(); enqueueCallback()3
setState()
将对组件 state 的更改排入队列批量推迟更新,并通知 React 需要使用更新后的 state 重新渲染此组件及其子组件。其实setState实际上不是异步,只是代码执行顺序不同,有了异步的感觉。
使用方法 setState(stateChange | updater [, callback])
(state, props) => stateChange
,返回基于 state 和 props 构建的新对象,将被浅层合并到新的 state 中使用 setState()
改变状态之后,立刻通过this.state拿不到最新的状态
可以使用 componentDidUpdate()
或者 setState(updater, callback)
中的回调函数 callback
保证在应用更新后触发,通常建议使用 componentDidUpdate()
多次setState()
函数调用产生的效果会合并
为了更好的感知性能,React 会在同一周期内会对多个 setState()
进行批处理。通过触发一次组件的更新来引发回流。后调用的 setState()
将覆盖同一周期内先调用 setState()
的值。所以如果是下一个 state 依赖前一个 state 的话,推荐给 setState()
传 function
onClick = () => {
this.setState({ quantity: this.state.quantity + 1 });
this.setState({ quantity: this.state.quantity + 1 });
}
// react中,这个方法最终会变成
Object.assign(
previousState,
{quantity: state.quantity + 1},
{quantity: state.quantity + 1},
...
)
同步更新
异步更新
react 参照版本 15.6.0
源码路径 src/isomorphic/modern/class/ReactBaseClasses.js
React组件继承自React.Component,而setState是React.Component的方法,因此对于组件来讲setState属于其原型方法
ReactComponent.prototype.setState = function(partialState, callback) {
this.updater.enqueueSetState(this, partialState);
if (callback) {
this.updater.enqueueCallback(this, callback, 'setState');
}
}
源码路径 src/renderers/shared/stack/reconciler/ReactUpdateQueue.js
这个文件导出了一个 ReactUpdateQueue 对象(React更新队列)
enqueueSetState: function(publicInstance, partialState) {
var internalInstance = getInternalInstanceReadyForUpdate(
publicInstance,
'setState',
);
if (!internalInstance) {
return;
}
var queue =
internalInstance._pendingStateQueue ||
(internalInstance._pendingStateQueue = []);
queue.push(partialState);
enqueueUpdate(internalInstance);
}
enqueueCallback: function(publicInstance, callback, callerName) {
ReactUpdateQueue.validateCallback(callback, callerName);
var internalInstance = getInternalInstanceReadyForUpdate(publicInstance);
if (!internalInstance) {
return null;
}
if (internalInstance._pendinGCallbacks) {
internalInstance._pendingCallbacks.push(callback);
} else {
internalInstance._pendingCallbacks = [callback];
}
enqueueUpdate(internalInstance);
}
源码路径 src/renderers/shared/stack/reconciler/ReactUpdates.js
如果处于批量更新模式,也就是 isBatchingUpdates 为 true 时,不进行state的更新操作,而是将需要更新的 component 添加到 dirtyComponents 数组中。
如果不处于批量更新模式,对所有队列中的更新执行 batchedUpdates 方法。
function enqueueUpdate(component) {
ensureInjected();
if (!batchingStrategy.isBatchingUpdates) {
batchingStrategy.batchedUpdates(enqueueUpdate, component);
return;
}
dirtyComponents.push(component);
if (component._updateBatchNumber == null) {
component._updateBatchNumber = updateBatchNumber + 1;
}
}
源码路径 src/renderers/shared/stack/reconciler/ReactDefaultBatchingStrategy.js
如果 isBatchingUpdates 为 true,当前正处于更新事务状态中,则将 Component 存入 dirtyComponent 中, 否则调用 batchedUpdates 处理,发起一个 transaction.perfORM()。
var transaction = new ReactDefaultBatchingStrategyTransaction();
var ReactDefaultBatchingStrategy = {
isBatchingUpdates: false,
batchedUpdates: function(callback, a, b, c, d, e) {
var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;
ReactDefaultBatchingStrategy.isBatchingUpdates = true;
if (alreadyBatchingUpdates) {
return callback(a, b, c, d, e);
} else {
return transaction.perform(callback, null, a, b, c, d, e);
}
},
};
源码路径 src/renderers/shared/stack/reconciler/ReactDefaultBatchingStrategy.js
Transaction 中注册了两个 wrapper,RESET_BATCHED_UPDATES
和 FLUSH_BATCHED_UPDATES
。
initialize 阶段,两个 wrapper 都是空方法,什么都不做。
close 阶段,RESET_BATCHED_UPDATES
将 isBatchingUpdates 设置为false;FLUSH_BATCHED_UPDATES
运行 flushBatchedUpdates 执行update。
var RESET_BATCHED_UPDATES = {
initialize: emptyFunction,
close: function() {
ReactDefaultBatchingStrategy.isBatchingUpdates = false;
},
};
var FLUSH_BATCHED_UPDATES = {
initialize: emptyFunction,
close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates),
};
ReactUpdates.flushBatchedUpdates() - ReactUpdates.runBatchedUpdates() - ReactCompositeComponent.performUpdateIfNecessary() - receiveComponent() + updateComponent()
runBatchedUpdates循环遍历dirtyComponents数组,主要干两件事:
receiveComponent 最后会调用 updateComponent
updateComponent 中会执行 React 组件存在期的生命周期方法,如 componentWillReceiveProps, shouldComponentUpdate, componentWillUpdate,render, componentDidUpdate。 从而完成组件更新的整套流程
在shouldComponentUpdate之前,执行了_processPendingState方法,该方法主要对state进行处理:
在一个生命周期内,在componentShouldUpdate执行之前,所有的state变化都会被合并,最后统一处理。
flushBatchedUpdates(); runBatchedUpdates()
源码路径 src/renderers/shared/stack/reconciler/ReactUpdates.js
var flushBatchedUpdates = function() {
while (dirtyComponents.length || asapEnqueued) {
if (dirtyComponents.length) {
var transaction = ReactUpdatesFlushTransaction.getPooled();
transaction.perform(runBatchedUpdates, null, 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++) {
// dirtyComponents中取出一个component
var component = dirtyComponents[i];
// 取出dirtyComponent中的未执行的callback
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);
}
// 执行updateComponent
ReactReconciler.performUpdateIfNecessary(
component,
transaction.reconcileTransaction,
updateBatchNumber,
);
// 执行dirtyComponent中之前未执行的callback
if (callbacks) {
for (var j = 0; j < callbacks.length; j++) {
transaction.callbackQueue.enqueue(
callbacks[j],
component.getPublicInstance(),
);
}
}
}
}
performUpdateIfNecessary()
源码路径 src/renderers/shared/stack/reconciler/ReactReconciler.js
performUpdateIfNecessary: function(
internalInstance,
transaction,
updateBatchNumber,
) {
internalInstance.performUpdateIfNecessary(transaction);
},
};
performUpdateIfNecessary()
源码路径 src/renderers/shared/stack/reconciler/ReactCompositeComponent.js
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()
源码路径 src/renderers/shared/stack/reconciler/ReactCompositeComponent.js
简单地说,一个 Transaction 就是将需要执行的 anyMethod 使用 wrapper 封装起来,在通过 Transaction 提供的 perform 方法执行,而在 perform 之前,先执行所有 wrapper 中的 initialize 方法,perform 之后再执行所有 wrapper 中的 close 方法。一组 initialize 及 close 方法称为一个 wrapper,Transaction 支持多个 wrapper 叠加。
React 中的 Transaction 提供了一个 Mixin 方便其他模块实现自己需要的事务。要实现自己的事务,需要额外实现一个抽象的 getTransactionWrappers()
接口,这个接口是 Transaction 用来获取所有 wrapper 的 initialize 和 close 方法,因此需要返回一个数组对象,每个对象分别有 key 为 initialize 和 close 的方法。
var Transaction = require('Transaction');
var emptyFunction = require('emptyFunction');
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, {
getTransactionWrappers: function() {
return TRANSACTION_WRAPPERS;
},
});
setState 流程还是很复杂的,设计也很精巧,避免了重复无谓的刷新组件,React大量运用了注入机制,这样每次注入的都是同一个实例化对象,防止多次实例化
开始执行事务初始化,运行,结束三个阶段
RESET_BATCHED_UPDATES FLUSH_BATCHED_UPDATES
这两个 wrapper 中的 close 方法FLUSH_BATCHED_UPDATES
在 close 阶段,flushBatchedUpdates 方法会循环遍历所有的 dirtyComponents ,调用 updateComponent 刷新组件,并执行它的 pendingCallbacks , 也就是 setState 中设置的 callback
组件挂载后,setState一般是通过DOM交互事件触发,如 click
ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping);
handleTopLevelImpl 是在这边调用DOM事件对应的回调方法
然后是setState()
dispatchEvent: function(topLevelType, nativeEvent) {
if (!ReactEventListener._enabled) {
return;
}
var bookKeeping = TopLevelCallbackBookKeeping.getPooled(
topLevelType,
nativeEvent,
);
try {
// Event queue being processed in the same cycle allows
// `preventDefault`.
ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping);
} finally {
TopLevelCallbackBookKeeping.release(bookKeeping);
}
}
以上就是React.js源码解析setState流程的详细内容,更多关于React setState流程的资料请关注编程网其它相关文章!
--结束END--
本文标题: React.js源码解析setState流程
本文链接: https://lsjlt.com/news/176800.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
2024-01-12
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0