React useTransition 源码
useTransition
是React18提供的新Hook,可让您在不阻塞 UI 的情况下更新状态。
const [isPending, startTransition] = useTransition();
isPending
: 告诉组件更新是否在pending中; 调用startTransition
就设置为true
,直到回调函数执行前才设置为false。startTransition
: 该函数用来将状态更新标记为过渡,同时通过回调来延迟更新状态。
源码
useTransition
在mount阶段调用mountTransition
,返回[pending, startTransition]
,调用startTransition
来实现功能。
function mountTransition(): [ boolean, (callback: () => void, options?: StartTransitionOptions) => void, ] { const stateHook = mountStateImpl((false: Thenable<boolean> | boolean)); const start = startTransition.bind( null, currentlyRenderingFiber, stateHook.queue, true, // pending状态,调用 startTransition 时,pending就为true false, // 当startTransition中callback要执行时pending的状态就设置为false,表示不是pending状态了。 ); const hook = mountWorkInProgressHook(); // 将 start 放在hook的memoizedState上,在更新阶段就从memoizedState上取,不会重新生成 hook.memoizedState = start; return [false, start]; // isPending的默认值为false }
startTransition
分为三次更新,下面标记为update1
、update2
、update3
;
update1
: 设置pending=true,表示处于pendin状态(以React事件优先级更新,优先级不低于ContinuousEventPriority
)update2
: 设置pending=false,表示处于pendin状态结束,开始处理业务(以TransitionLanes
优先级)udpate3
: 调用回调函数,真正处理业务的地方(以TransitionLanes
优先级)
startTransition
的流程如下:
- 保存以前的更新优先级
previousPriority
、prevTransition
;设置本次更新的优先级(最低为持续更新ContinuousEventPriority
优先级,保证update1
能尽快更新); - 设置
ReactCurrentBatchConfig.transition = null
,保证第一次更新是以高优先级更新(标记为update1
的地方),pending
状态就会设置为true
;同时设置ReactCurrentBatchConfig.transition = currentTransition
,后续的update2
、udpate3
就是通过该值确定为TransitionLanes
优先级。 - 接着执行
dispatchSetState(fiber, queue, finishedState)
(标记为update2
),设置pending
为false
;优先级为TransitionLanes
。 - 执行
callback
(标记为update3
),优先级和udpate2
一样为TransitionLanes
优先级(TransitionLanes
优先级有15种,所以update2和update3优先级是不同的),所以状态不会立即更新,要等到其他高优先级的任务先执行, - 还原优先级。
可以看出update1
分配了不低于ContinuousEventPriority
的优先级(比如:如果实在click
事件中调用就是DiscreteEventPriority
优先级),update2
、update3
分配了TransitionLanes
优先级,
PS: 如果用户一直调用startTransition
,新的udpate1
就会打断上一次的update2
、udpate3
,pending
状态就一直为true
,直到知道上一次的udpate2
、update3
过期。
function startTransition<S>( fiber: Fiber, queue: UpdateQueue<S | Thenable<S>, BasicStateAction<S | Thenable<S>>>, pendingState: S, finishedState: S, callback: () => mixed, // 业务设置的回调函数 options?: StartTransitionOptions, ): void { // 保存以前的优先级,设置持续事件优先级,最后恢复成previousPriority优先级 const previousPriority = getCurrentUpdatePriority(); setCurrentUpdatePriority( higherEventPriority(previousPriority, ContinuousEventPriority), ); // 保存以前的 transition, 设置新的transition;最后恢复prevTransition const prevTransition = ReactCurrentBatchConfig.transition; const currentTransition: BatchConfigTransition = { _callbacks: new Set<(BatchConfigTransition, mixed) => mixed>(), }; if (enableAsyncActions) { ReactCurrentBatchConfig.transition = currentTransition; dispatchOptimisticSetState(fiber, false, queue, pendingState); // pendingState } else { ReactCurrentBatchConfig.transition = null; dispatchSetState(fiber, queue, pendingState); // update1: pendingState: 在mountTransition中设置为true,表示处理中pending ReactCurrentBatchConfig.transition = currentTransition; // 后面的upadte2、udpate3 的优先级就是transition的优先级,比较低,可以被打断,就不会阻塞高优先级(比较用户输入,点击事件)。 } // .... try { if (enableAsyncActions) { // .... } else { // // 关闭pending,表示开始处理 // 会根据ReactCurrentBatchConfig.transition来判断是否存在transition,同时给本次更新分配transition的优先级 dispatchSetState(fiber, queue, finishedState); // update2: // 真正执行业务回调的地方 callback(); // udpate3: } } catch (error) { // .... } finally { // 还原优先级,因为前端将优先级设置为transition的优先级了 setCurrentUpdatePriority(previousPriority); // 还原transition ReactCurrentBatchConfig.transition = prevTransition; // .... } }
transition优先级分配机制
dispatchSetState
中通过requestUpdateLane()
获取优先级。
上面udpate2
、update3
的优先级与udpate1
不同的的原因在于requestUpdateLane
中通过ReactCurrentBatchConfig.transition
来判断是否分配TransitionLanes
优先级;
export function requestUpdateLane(fiber: Fiber): Lane { // .... const transition = requestCurrentTransition(); // 就是获取 ReactCurrentBatchConfig.transition,在startTransition里面添加的 if (transition !== null) {// 当前存在的transition, 就获取对应的lane const actionScopeLane = peekEntangledActionLane(); return actionScopeLane !== NoLane ? actionScopeLane : requestTransitionLane(transition); // requestTransitionLane 中为transition分配优先级 } // ... }
requestTransitionLane
中通过claimNextTransitionLane
获取优先级。
transition
的优先级一共有15种,初始值为TransitionLane1
,每次分配一个比上次低一级的优先级直到TransitionLane15
,如果分配完了就重新从TransitionLane1
开始分配,一直循环。
export function claimNextTransitionLane(): Lane { // nextTransitionLane的初始值为 TransitionLane1 const lane = nextTransitionLane; // 每次向左移一位 nextTransitionLane <<= 1; if ((nextTransitionLane & TransitionLanes) === NoLanes) { // 如果分配完了就从 TransitionLane1 开始重头开始分配 nextTransitionLane = TransitionLane1; } return lane; }
React优先级
- lane优先级(共31种)
export const NoLanes: Lanes = /* */ 0b0000000000000000000000000000000; export const NoLane: Lane = /* */ 0b0000000000000000000000000000000; export const SyncHydrationLane: Lane = /* */ 0b0000000000000000000000000000001; export const SyncLane: Lane = /* */ 0b0000000000000000000000000000010; export const SyncLaneIndex: number = 1; export const InputContinuousHydrationLane: Lane = /* */ 0b0000000000000000000000000000100; export const InputContinuousLane: Lane = /* */ 0b0000000000000000000000000001000; export const DefaultHydrationLane: Lane = /* */ 0b0000000000000000000000000010000; export const DefaultLane: Lane = /* */ 0b0000000000000000000000000100000; const TransitionHydrationLane: Lane = /* */ 0b0000000000000000000000001000000; const TransitionLanes: Lanes = /* */ 0b0000000001111111111111110000000; const TransitionLane1: Lane = /* */ 0b0000000000000000000000010000000; const TransitionLane2: Lane = /* */ 0b0000000000000000000000100000000; const TransitionLane3: Lane = /* */ 0b0000000000000000000001000000000; const TransitionLane4: Lane = /* */ 0b0000000000000000000010000000000; const TransitionLane5: Lane = /* */ 0b0000000000000000000100000000000; const TransitionLane6: Lane = /* */ 0b0000000000000000001000000000000; const TransitionLane7: Lane = /* */ 0b0000000000000000010000000000000; const TransitionLane8: Lane = /* */ 0b0000000000000000100000000000000; const TransitionLane9: Lane = /* */ 0b0000000000000001000000000000000; const TransitionLane10: Lane = /* */ 0b0000000000000010000000000000000; const TransitionLane11: Lane = /* */ 0b0000000000000100000000000000000; const TransitionLane12: Lane = /* */ 0b0000000000001000000000000000000; const TransitionLane13: Lane = /* */ 0b0000000000010000000000000000000; const TransitionLane14: Lane = /* */ 0b0000000000100000000000000000000; const TransitionLane15: Lane = /* */ 0b0000000001000000000000000000000; const RetryLanes: Lanes = /* */ 0b0000011110000000000000000000000; const RetryLane1: Lane = /* */ 0b0000000010000000000000000000000; const RetryLane2: Lane = /* */ 0b0000000100000000000000000000000; const RetryLane3: Lane = /* */ 0b0000001000000000000000000000000; const RetryLane4: Lane = /* */ 0b0000010000000000000000000000000; export const SomeRetryLane: Lane = RetryLane1; export const SelectiveHydrationLane: Lane = /* */ 0b0000100000000000000000000000000; const NonIdleLanes: Lanes = /* */ 0b0000111111111111111111111111111; export const IdleHydrationLane: Lane = /* */ 0b0001000000000000000000000000000; export const IdleLane: Lane = /* */ 0b0010000000000000000000000000000; export const OffscreenLane: Lane = /* */ 0b0100000000000000000000000000000; export const DeferredLane: Lane = /* */ 0b1000000000000000000000000000000;
- 事件优先级
优先级依次降低,事件优先级也是包含在lane优先级中的。
// 离散事件优先级: input、点击事件等 export const DiscreteEventPriority: EventPriority = SyncLane; // 连续事件优先级:比如鼠标移动、拖拽d等 export const ContinuousEventPriority: EventPriority = InputContinuousLane; // 默认优先级:settimeout等 export const DefaultEventPriority: EventPriority = DefaultLane; // 闲置事件优先级 export const IdleEventPriority: EventPriority = IdleLane;
React在为每种事件都分配了不同的优先级。(下面为其中一小部分)
export function getEventPriority(domEventName: DOMEventName): EventPriority { switch (domEventName) { case 'cancel': case 'click': case 'popstate': case 'select': // .... case 'selectstart': return DiscreteEventPriority; case 'drag': case 'mousemove': case 'mouseout': // ... case 'mouseover': return ContinuousEventPriority; // ... default: return DefaultEventPriority; } }
- 调度优先级
优先级依次降低
// 立即优先级 export const ImmediatePriority = 1; // 用户阻塞优先级 export const UserBlockingPriority = 2; // 普通优先级 export const NormalPriority = 3; // 低优先级 export const LowPriority = 4; // 空闲优先级 export const IdlePriority = 5;
在调度前会通过update的优先级来确定调度的优先级,先把update的lane转成事件优先级,再把事件优先级转换成调度优先级,在调度时根据调度优先级确定任务的过期时间,从而确定任务的执行时机。
let schedulerPriorityLevel; switch (lanesToEventPriority(nextLanes)) { case DiscreteEventPriority: schedulerPriorityLevel = ImmediateSchedulerPriority; break; case ContinuousEventPriority: schedulerPriorityLevel = UserBlockingSchedulerPriority; break; case DefaultEventPriority: schedulerPriorityLevel = NormalSchedulerPriority; break; case IdleEventPriority: schedulerPriorityLevel = IdleSchedulerPriority; break; default: schedulerPriorityLevel = NormalSchedulerPriority; break; } const newCallbackNode = scheduleCallback( schedulerPriorityLevel, performConcurrentWorkOnRoot.bind(null, root), );