react18 的 setState 还是 isBatchingUpdates 嘛?
导言:
react-dom版本: '18.0.0-e3b76a85c-20210802'
react版本:'18.0.0-9f88b5355-20210728'
-
class 组件调用setState,其底层是通过ReactDOM 源码中的
classComponentUpdater.enqueueSetState方法,之后执行enqueueUpdate方法的updateQueue入队操作,而后执行scheduleUpdateOnFiber进入批量更新操作,这里update的创建以及入队,不作讲解var classComponentUpdater = { enqueueSetState: function (inst, payload, callback) { var fiber = get(inst); var eventTime = requestEventTime(); var lane = requestUpdateLane(fiber); var update = createUpdate(eventTime, lane); update.payload = payload; if (callback !== undefined && callback !== null) { update.callback = callback; } enqueueUpdate(fiber, update); // 将创建的update入队 scheduleUpdateOnFiber(fiber, lane, eventTime); // 主要操作 }, } -
在
scheduleUpdateOnFiber中会调用ensureRootIsScheduled方法,目的就是检测root的更新是否放到了scheduler(调度器)中 -
ensureRootIsScheduled阶段:
-
获取当前执行上下文的执行优先级
var newCallbackPriority = getHighestPriorityLane(nextLanes); -
获取已经在执行的优先级
var existingCallbackPriority = root.callbackPriorityvar existingCallbackPriority = root.callbackPriority; -
如果当前执行上下文的优先级 等于已经在执行优先级,便会
return,这里就是为什么在react 合成事件中多次调用setState,只会被执行一次的原因了,因为后几个加到updateQueue后被return了if (existingCallbackPriority === newCallbackPriority && !( ReactCurrentActQueue.current !== null && existingCallbackNode !== fakeActCallbackNode)) { return; } -
如果#3的条件不满足,则会向
scheduler(调度器)中添加performConcurrentWorkOnRoot或者performSyncWorkOnRootif (newCallbackPriority === SyncLane) { // Legacy 模式 if (root.tag === LegacyRoot) { if ( ReactCurrentActQueue.isBatchingLegacy !== null) { ReactCurrentActQueue.didScheduleLegacyUpdate = true; } scheduleLegacySyncCallback(performSyncWorkOnRoot.bind(null, root)); // 将 performSyncWorkOnRoot 加入SyncQueue, 合并update,待后续执行 } else { scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root)); } { if ( ReactCurrentActQueue.current !== null) { ReactCurrentActQueue.current.push(flushSyncCallbacks); } else { scheduleMicrotask(flushSyncCallbacks); // 将flushSyncCallbacks 加入微任务队列, flushSyncCallbacks 便会将 syncQueue 加入 Scheduler.scheduleCallbac } } newCallbackNode = null; } else { // concurrent 模式 var schedulerPriorityLevel; switch (lanesToEventPriority(nextLanes)) { case DiscreteEventPriority: schedulerPriorityLevel = ImmediatePriority; break; case ContinuousEventPriority: schedulerPriorityLevel = UserBlockingPriority; break; case DefaultEventPriority: schedulerPriorityLevel = NormalPriority; break; case IdleEventPriority: schedulerPriorityLevel = IdlePriority; break; default: schedulerPriorityLevel = NormalPriority; break; } newCallbackNode = scheduleCallback$1(schedulerPriorityLevel, performConcurrentWorkOnRoot.bind(null, root)); } -
最后会在root上设置
callbackPriority、callbackNoderoot.callbackPriority = newCallbackPriority; root.callbackNode = newCallbackNode;
-
总结:setState 不是异步的,只是被加入到了scheduler的更新队列,会在下一轮任务循环中更新,导致在多次在同步任务中多次调用setState会被合并,而非合成事件则会在其他的任务队列中执行,所以不会合并在一起