图解React图解React
  • 原理解析
  • 高频算法
  • 面试题
⌘ K
setState
React中key的作用
interview
使用说明
Copyright © 2023 | Powered by dumi

React 中, setState 是同步还是异步

所谓同步还是异步指的是调用 setState 之后是否马上能得到最新的 state

不仅仅是setState了, 在对 function 类型组件中的 hook 进行操作时也是一样, 最终决定setState是同步渲染还是异步渲染的关键因素是ReactFiberWorkLoop工作空间的执行上下文.

具体代码如下:

export function scheduleUpdateOnFiber(
fiber: Fiber,
expirationTime: ExpirationTime,
) {
const priorityLevel = getCurrentPriorityLevel();
if (expirationTime === Sync) {
if (
// Check if we're inside unbatchedUpdates
(executionContext & LegacyUnbatchedContext) !== NoContext &&
// Check if we're not already rendering
(executionContext & (RenderContext | CommitContext)) === NoContext
) {
performSyncWorkOnRoot(root);
} else {
ensureRootIsScheduled(root);
schedulePendingInteractions(root, expirationTime);
if (executionContext === NoContext) {
// Flush the synchronous work now, unless we're already working or inside
// a batch. This is intentionally inside scheduleUpdateOnFiber instead of
// scheduleCallbackForFiber to preserve the ability to schedule a callback
// without immediately flushing it. We only do this for user-initiated
// updates, to preserve historical behavior of legacy mode.
flushSyncCallbackQueue();
}
}
} else {
// Schedule a discrete update but only if it's not Sync.
if (
(executionContext & DiscreteEventContext) !== NoContext &&
// Only updates at user-blocking priority or greater are considered
// discrete, even inside a discrete event.
(priorityLevel === UserBlockingPriority ||
priorityLevel === ImmediatePriority)
) {
// This is the result of a discrete event. Track the lowest priority
// discrete update per root so we can flush them early, if needed.
if (rootsWithPendingDiscreteUpdates === null) {
rootsWithPendingDiscreteUpdates = new Map([[root, expirationTime]]);
} else {
const lastDiscreteTime = rootsWithPendingDiscreteUpdates.get(root);
if (
lastDiscreteTime === undefined ||
lastDiscreteTime > expirationTime
) {
rootsWithPendingDiscreteUpdates.set(root, expirationTime);
}
}
}
// Schedule other updates after in case the callback is sync.
ensureRootIsScheduled(root);
schedulePendingInteractions(root, expirationTime);
}
}

可以看到, 是否同步渲染调度决定代码是flushSyncCallbackQueue(). 进入该分支的条件:

  1. 必须是legacy模式, concurrent模式下expirationTime不会为Sync
  2. executionContext === NoContext, 执行上下文必须要为空.

两个条件缺一不可.

结论

同步:

  1. 首先在legacy模式下
  2. 在执行上下文为空的时候去调用setState
    • 可以使用异步调用如setTimeout, Promise, MessageChannel等
    • 可以监听原生事件, 注意不是合成事件, 在原生事件的回调函数中执行 setState 就是同步的

异步:

  1. 如果是合成事件中的回调, executionContext |= DiscreteEventContext, 所以不会进入, 最终表现出异步
  2. concurrent 模式下都为异步

演示示例

在看一个 concurrent 模式下的例子, 相同的代码都为异步 render:

Edit boring-faraday-m7jtx

当前count=0