export function scheduleUpdateOnFiber(
fiber: Fiber,
lane: Lane,
eventTime: number,
) {
if (lane === SyncLane) {
// legacy或blocking模式
if (
(executionContext & LegacyUnbatchedContext) !== NoContext &&
(executionContext & (RenderContext | CommitContext)) === NoContext
) {
performSyncWorkOnRoot(root);
} else {
ensureRootIsScheduled(root, eventTime); // 注册回调任务
if (executionContext === NoContext) {
flushSyncCallbackQueue(); // 取消schedule调度 ,主动刷新回调队列,
}
}
} else {
// concurrent模式
ensureRootIsScheduled(root, eventTime);
}
}
当lane === SyncLane
也就是 legacy 或 blocking 模式中, 注册完回调任务之后(ensureRootIsScheduled(root, eventTime)
), 如果执行上下文为空, 会取消 schedule 调度, 主动刷新回调队列flushSyncCallbackQueue()
.
这里包含了一个热点问题(setState到底是同步还是异步
)的标准答案:
- 如果逻辑进入
flushSyncCallbackQueue
(executionContext === NoContext
), 则会主动取消调度, 并刷新回调, 立即进入fiber树
构造过程. 当执行setState
下一行代码时,fiber树
已经重新渲染了, 故setState
体现为同步. - 正常情况下, 不会取消
schedule调度
. 由于schedule调度
是通过MessageChannel
触发(宏任务), 故体现为异步.
React hooks相关:
hook本质是将直接修改组件的state,改成了一个消息队列,通过useState中的set方法将修改事件推到队列中,react会进行合并和修改,然后自动触发setState。
useEffect在使用时候需要注意不能在外部使用判断,因为如果有判断的话,会导致react无法找到它而引发报错。第一个参数是回调函数,当不传递第二个参数的时候,每次render后都会执行,传递空数组的话,生命周期和componentDidMount一致,只会在组件挂载后执行一次,传递相关变量,那么会在第一次挂载和每次变量变化时执行。回调函数返回一个函数的话,这个函数对应的是componentWillUnmount钩子。
useMemo(),缓存计算函数的结果,只有当第二个参数关心的变量发生变化时候才会再次执行计算,因为每次render都会让整个函数重新运行,如果不进行useMemo()缓存结果的话,那么每次render都会进行运算,会造成性能上的浪费。