说明#
- 本文基于 v18.1.0 进行分析。
- 阅读本文需先阅读 React Hooks: hooks 链表、React Hooks: useState 分析。
- 调试基于 React Hooks: useState 内关于插队的代码。
TLNR#
该 Hook 的返回值 startTransition 接收一个函数,函数内触发的更新都会被标记为低优先级,从而允许其它优先级更高的更新优先被处理。
mount 阶段的 useTranstion#
对调试代码中的 useTranstion 打上断点,我们会在 mount 阶段时进入 mountTransition 这一函数。
function mountTransition(): [
boolean,
(callback: () => void, options?: StartTransitionOptions) => void,
] {
// useState 那篇文章内有介绍过,不再啰嗦
// 但这里可以看出 useTranstion 也会存在一个对应的 hook 实例(尽管是基于useState实现的)
const [isPending, setPending] = mountState(false);
// The `start` method never changes.
// 此处是关键实现,下面会分析
const start = startTransition.bind(null, setPending);
// hooks 链表里介绍过,不再啰嗦
const hook = mountWorkInProgressHook();
hook.memoizedState = start;
return [isPending, start];
}
从代码中不难发现,关键的分析点在于 startTransition,因此我们看看它的实现是什么。
// 删掉了一些不重要的代码
function startTransition(setPending, callback, options) {
// 创建变量缓存当前的 update 优先级
// 该优先级可被 getCurrentUpdatePriority 与 setCurrentUpdatePriority访问到
const previousPriority = getCurrentUpdatePriority();
setCurrentUpdatePriority(
// 比较当前优先级与 ContinuousEventPriority 间谁更优先,并设置为当前的优先级
higherEventPriority(previousPriority, ContinuousEventPriority),
);
// 这里通过调用 useState 的 dispatch 函数,创建了较高优先级的 update 实例
setPending(true);
// 直接修改 ReactCurrentBatchConfig 的值,这将影响后续生成 update 的优先级
// 具体的代码可以看生成 update 实例时调用的 requestUpdateLane 函数
const prevTransition = ReactCurrentBatchConfig.transition;
ReactCurrentBatchConfig.transition = {};
const currentTransition = ReactCurrentBatchConfig.transition;
if (enableTransitionTracing) {
if (options !== undefined && options.name !== undefined) {
ReactCurrentBatchConfig.transition.name = options.name;
ReactCurrentBatchConfig.transition.startTime = now();
}
}
// 总的来说,上面对 ReactCurrentBatchConfig 的操作将会影响下面的 setPending 与 callback 内生成的 update 实例的优先级,而其优先级必然是低于第一次调用 setPending 所生成的 update 实例的优先级的
// 而低优先级的 update 又会在处理状态更新时被跳过,这里在 useState 的文章里也有提及
try {
setPending(false);
callback();
} finally {
// 这里是重置的操作,将优先级恢复到原先的状态
setCurrentUpdatePriority(previousPriority);
ReactCurrentBatchConfig.transition = prevTransition;
...
}
}
总结#
很简单的一个钩子,总的原理便是将包裹在 callback 内所生成的更新打上低优先级的标记,从而让其他的更新优先处理。