Explanation#
- This article is based on v18.1.0.
- To read this article, you need to first read React Hooks: hooks chain and React Hooks: useState analysis.
- Debugging is based on the code related to queuing inside React Hooks: useState.
TLNR#
The return value of this Hook, startTransition, accepts a function. Any updates triggered within this function will be marked as low priority, allowing higher priority updates to be processed first.
useTransition during the mount phase#
By setting breakpoints in the useTransition code in the debugging code, we can enter the mountTransition function during the mount phase.
function mountTransition(): [
boolean,
(callback: () => void, options?: StartTransitionOptions) => void,
] {
// This has been explained in the useState article, so I won't go into detail here.
// But it can be seen that useTransition also has a corresponding hook instance (although it is implemented based on useState).
const [isPending, setPending] = mountState(false);
// The `start` method never changes.
// This is the key implementation, which will be analyzed below.
const start = startTransition.bind(null, setPending);
// This has been explained in the hooks chain article, so I won't go into detail here.
const hook = mountWorkInProgressHook();
hook.memoizedState = start;
return [isPending, start];
}
From the code, it is not difficult to find that the key analysis point lies in startTransition, so let's see what its implementation is.
// Some unimportant code has been removed
function startTransition(setPending, callback, options) {
// Create a variable to cache the current update priority
// This priority can be accessed by getCurrentUpdatePriority and setCurrentUpdatePriority
const previousPriority = getCurrentUpdatePriority();
setCurrentUpdatePriority(
// Compare the current priority with ContinuousEventPriority to determine which one is higher, and set it as the current priority
higherEventPriority(previousPriority, ContinuousEventPriority),
);
// By calling the dispatch function of useState here, a higher priority update instance is created
setPending(true);
// Modify the value of ReactCurrentBatchConfig directly, which will affect the priority of the subsequent generated update instances
// The specific code can be seen in the requestUpdateLane function called when generating update instances
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();
}
}
// In summary, the operations on ReactCurrentBatchConfig above will affect the priority of the update instances generated inside setPending and callback, and their priority will definitely be lower than the priority of the update instance generated by the first call to setPending
// And the low priority updates will be skipped during the state update process, which is also mentioned in the useState article
try {
setPending(false);
callback();
} finally {
// This is the reset operation, restoring the priority to its original state
setCurrentUpdatePriority(previousPriority);
ReactCurrentBatchConfig.transition = prevTransition;
...
}
}
Summary#
A very simple hook, the main principle is to mark the updates generated within the callback with a low priority flag, so that other updates can be processed first.