説明#
- この記事は v18.1.0 を基に分析されています。
- この記事を読むには、まずReact Hooks: hooks 链表とReact Hooks: useState 分析を読む必要があります。
- デバッグは React Hooks: useState 内のキューイングに関するコードに基づいています。
TLNR#
このフックの戻り値である startTransition 関数は、関数内でトリガされるすべての更新を低優先度としてマークし、他の高優先度の更新が優先的に処理されるようにします。
mount フェーズの useTransition#
デバッグコードの useTransition にブレークポイントを設定すると、mount フェーズで mountTransition 関数に入ることがわかります。
function mountTransition(): [
boolean,
(callback: () => void, options?: StartTransitionOptions) => void,
] {
// useStateの記事で説明されているので、ここでは省略します
// ただし、ここでuseTransitionに対応するフックのインスタンスが存在することがわかります(useStateをベースに実装されているため)
const [isPending, setPending] = mountState(false);
// `start`メソッドは変更されません。
// これが重要な実装であり、以下で分析します
const start = startTransition.bind(null, setPending);
// hooksのリストについては、すでに説明されているので省略します
const hook = mountWorkInProgressHook();
hook.memoizedState = start;
return [isPending, start];
}
コードから明らかなように、重要な分析ポイントは startTransition にありますので、その実装を見てみましょう。
// いくつかの重要でないコードを削除しました
function startTransition(setPending, callback, options) {
// 現在の更新の優先度をキャッシュする変数を作成します
// この優先度はgetCurrentUpdatePriorityとsetCurrentUpdatePriorityでアクセスできます
const previousPriority = getCurrentUpdatePriority();
setCurrentUpdatePriority(
// 現在の優先度とContinuousEventPriorityを比較し、より優先度の高いものを現在の優先度として設定します
higherEventPriority(previousPriority, ContinuousEventPriority),
);
// ここではuseStateのdispatch関数を呼び出すことで、より高い優先度の更新インスタンスを作成しています
setPending(true);
// ReactCurrentBatchConfigの値を直接変更することで、後続の更新の優先度に影響を与えます
// 具体的なコードは、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 内で生成される更新に低優先度のマークを付け、他の更新を優先的に処理することです。