banner
IWSR

IWSR

我永远喜欢志喜屋梦子!

React Hooks: useTransition

说明#

  1. 本文基于 v18.1.0 进行分析。
  2. 阅读本文需先阅读 React Hooks: hooks 链表React Hooks: useState 分析
  3. 调试基于 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 内所生成的更新打上低优先级的标记,从而让其他的更新优先处理。

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。