useCallback
挂载阶段:mountCallback
javascript
function mountCallback(callback, deps) {
const hook = mountWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
hook.memoizedState = [callback, nextDeps];
return callback;
}
作用
- 创建空白
Hook节点,串联进组件全局Hooks单向链表 - 规范化依赖数组
- 将
[callback, nextDeps]存入hook.memoizedState持久保存 - 返回本次传入的原始回调
更新阶段:updateCallback
javascript
function updateCallback(callback, deps) {
const hook = updateWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
const prevState = hook.memoizedState;
if (nextDeps !== null) {
const prevDeps = prevState[1];
if (areHookInputsEqual(nextDeps, prevDeps)) {
return prevState[0];
}
}
hook.memoizedState = [callback, nextDeps];
return callback;
}
作用
- 同步匹配、克隆
WIP Hook,推进新旧Hook双游标 - 规范化依赖数组
- 取出上一轮渲染缓存的 旧回调, 旧依赖
- 用户传入了依赖数组,执行对比逻辑
- 取出上一轮依赖
- 浅对比新旧依赖是否完全相等
- 依赖无变化,直接返回上一轮缓存的旧回调,不更新引用
- 依赖变化 / 无依赖数组,覆盖缓存为新回调, 新依赖
- 返回全新回调函数
设计意义
引用稳定缓存优化(useCallback 核心价值) :依赖不变时复用旧函数引用,传给 memo 子组件不会触发无效重渲染,减少调和开销
useMemo
挂载阶段:mountMemo
javascript
function mountMemo(
nextCreate,
deps,
) {
const hook = mountWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
const nextValue = nextCreate();
hook.memoizedState = [nextValue, nextDeps];
return nextValue;
}
作用
- 新建标准化空白
Hook节点,通过全局workInProgressHook游标追加到Hooks链表尾部;首个Hook直接挂载到Fiber.memoizedState - 规范化依赖
- 执行用户传入的计算函数,得到初始缓存值
- 将
[nextValue, nextDeps]存入Hook持久缓存 - 返回初次计算出的值
更新阶段:updateMemo
javascript
function updateMemo(
nextCreate,
deps,
) {
const hook = updateWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
const prevState = hook.memoizedState;
if (nextDeps !== null) {
const prevDeps = prevState[1];
if (areHookInputsEqual(nextDeps, prevDeps)) {
return prevState[0];
}
}
const nextValue = nextCreate();
hook.memoizedState = [nextValue, nextDeps];
return nextValue;
}
作用
- 同步推进
currentHook(旧Fiber链表游标)与workInProgressHook(WIP链表游标),克隆旧Hook生成WIP副本 - 规范化依赖数组
- 取出上一轮缓存
[prevState, prevDeps] - 存在依赖数组,执行依赖对比逻辑
- 依赖完全不变,直接复用旧缓存值,不重新执行计算函数
- 依赖变化 / 无依赖,重新执行计算函数
- 覆盖
Hook缓存,存入[nextValue, nextDeps] - 返回全新计算值
设计意义
核心性能优化:依赖不变跳过昂贵计算。复杂数组遍历、大对象构造、多层数据转换等开销大的逻辑,仅依赖变化时才执行;大幅降低重复渲染计算开销
useRef
挂载阶段:mountRef
javascript
function mountRef(initialValue) {
const hook = mountWorkInProgressHook();
const ref = {current: initialValue};
hook.memoizedState = ref;
return ref;
}
作用
- 创建空白
Hook节点,串联进当前组件Hooks单向链表 - 构建标准
ref可变容器对象,current绑定初始值,引用地址永久不变(挂载阶段只创建一次) - 将
ref对象存入Hook的memoizedState持久保存 - 返回
ref容器给开发者使用
更新阶段:updateRef
javascript
function updateRef(initialValue) {
const hook = updateWorkInProgressHook();
return hook.memoizedState;
}
作用
- 同步匹配、克隆
WIP Hook,推进新旧Hook双游标 - 返回上一轮缓存的
ref对象
设计意义
- 稳定引用容器设计 :挂载阶段仅创建一次
{ current }对象,后续所有更新渲染不会重新生成,引用永远不变。开发者可跨渲染周期读写.current,修改内部值不会触发调和、重渲染,适合存DOM、定时器、临时变量
useContext
挂载阶段和更新阶段的底层实现 readContext,根据渲染环境读取隔离的上下文快照值,在校验当前处于合法组件渲染流后,以单向链表形式记录组件所有 Context 依赖并持久绑定到 Fiber,打上上下文传播标记,为后续 Provider 值变更时精准调度组件重渲染提供底层依赖追踪能力
javascript
export function readContext(context) {
return readContextForConsumer(currentlyRenderingFiber, context);
}
// useContext / 类组件 static contextType 底层读取上下文的核心工具函数
function readContextForConsumer(
consumer, // 当前正在渲染的消费组件 Fiber(函数 / 类组件)
context,
) {
// 区分客户端 DOM 渲染 / SSR 水合渲染
const value = isPrimaryRenderer
? context._currentValue // 主渲染器(客户端 DOM)存储的上下文值
: context._currentValue2; // 次渲染器(SSR/hydration 水合阶段)存储的上下文值
const contextItem = {
context,
memoizedValue: value,
next: null,
};
if (lastContextDependency === null) {
if (consumer === null) {
throw new Error(
'Context can only be read while React is rendering. ' +
'In classes, you can read it in the render method or getDerivedStateFromProps. ' +
'In function components, you can read it directly in the function body, but not ' +
'inside Hooks like useReducer() or useMemo().',
);
}
lastContextDependency = contextItem;
consumer.dependencies = {
lanes: NoLanes, // 该上下文变更对应的优先级车道
firstContext: contextItem, // 依赖链表头节点
};
// Fiber 标记,代表该组件存在上下文依赖,Context 更新需要向上向下传播更新信号
consumer.flags |= NeedsPropagation;
} else {
lastContextDependency = lastContextDependency.next = contextItem;
}
return value;
}
作用
- 根据渲染器类型读取对应上下文快照值:避免水合阶段上下文值错乱、服务端 / 客户端渲染不一致
- 浏览器客户端
DOM渲染,读取_currentValue SSR/ 水合阶段,读取隔离的_currentValue2
- 浏览器客户端
- 创建单个上下文依赖链表节点,缓存本次读取的
context与value,下一轮更新时对比该值,判断是否需要重渲染 - 当前组件还没有任何上下文依赖(链表为空)
- 安全校验:无
consumer代表不在渲染阶段读取Context,直接抛错 - 将当前节点设为链表头(单向上下文依赖链表),更新全局尾游标
- 初始化
Fiber的上下文依赖容器,挂载链表头 - 给Fiber打上上下文传播标记,标记该
Fiber存在Context依赖;当上层Provider value变更时,调度器会识别该标记,向下传播更新信号,触发组件重渲染
- 安全校验:无
- 已有上下文依赖,追加到链表尾部,更新尾游标
- 返回本次读取到的上下文值给组件使用
设计意义
- 单向链表统一管理多
Context依赖 :组件同时依赖多个Context时,通过单向链表串联所有ContextDependency节点,O (1) 尾部插入,统一存储在fiber.dependencies.firstContext;调和阶段只需遍历一条链表即可对比全部上下文值变更 - 标记驱动更新传播 :
NeedsPropagation Fiber位标记作为快速筛选条件。Provider更新时,仅遍历带有该标记的子组件,跳过无上下文依赖的子树,优化Context更新调度性能 - 快照缓存实现值对比逻辑 :每个依赖节点存储本次渲染
memoizedValue,下一轮调和时拿新Context值与快照对比,值不变则跳过组件重渲染,实现Context精准细粒度更新 - 依赖存储与组件
Fiber强绑定 :所有上下文依赖挂载在消费组件自身Fiber,而非Context对象上;一个Context可被无数组件消费,每个组件独立维护自己的依赖链表,多组件消费同一Context互不干扰
useImperativeHandle
挂载阶段:mountImperativeHandle
本质是带 ref 自动依赖的特殊 useLayoutEffect,完全复用 Effect 环形链表存储逻辑,不新增独立 Hook 存储结构,底层与 useLayoutEffect 共用一套副作用入队、遍历、执行机制。在组件初次挂载时自动将 ref 并入依赖数组,配置静态 Layout 副作用标记,commit 阶段可快速跳过无副作用子树,复用 useLayoutEffect 底层 mountEffectImpl 创建同步布局 Effect,预绑定用户实例创建函数与 ref,保证 DOM 更新同步后赋值 ref 自定义句柄,依托通用副作用底层实现 ref 自动依赖、同步执行、自动销毁清理完整能力
javascript
function mountImperativeHandle(
ref,
create,
deps,
) {
// 1. 构造包含 ref 的完整副作用依赖数组
const effectDeps =
deps !== null && deps !== undefined ? deps.concat([ref]) : null;
// 2. 定义该副作用对应的 Fiber 位标记
let fiberFlags = UpdateEffect | LayoutStaticEffect;
// 3. 复用 LayoutEffect 底层创建同步副作用
mountEffectImpl(
fiberFlags,
HookLayout, // 区分这是 Layout 同步副作用(同 useLayoutEffect)
imperativeHandleEffect.bind(null, create, ref),
effectDeps,
);
}
作用
- 构造合并
ref的依赖数组:ref是外部传入的引用对象,ref发生变化(父组件传递新ref)时,必须重新调用create生成新实例覆盖ref- 用户传入有效依赖数组
deps:原依赖数组末尾追加ref,生成新数组[...deps, ref] - 用户不传依赖(
deps = null / undefined):直接赋值null(每轮渲染都执行副作用)
- 用户传入有效依赖数组
- 定义
Fiber副作用位标记:区分普通LayoutEffect。useLayoutEffect仅打LayoutEffect,useImperativeHandle额外带静态标记,长期稳定存在无需反复重新标记UpdateEffect:Mutation阶段相关标记,代表存在ref更新类DOM/ 实例副作用LayoutStaticEffect:静态Layout副作用标记,挂载时永久标记在Fiber,subtreeFlags向上冒泡;commit阶段通过LayoutMask快速判断子树有无Layout副作用,跳过无副作用子树遍历,做性能短路优化
- 调用统一
Layout副作用底层mountEffectImpl- 绑定句柄生成函数与
ref,创建同步执行的Effect commit Layout阶段执行函数imperativeHandleEffect,内部调用create()获取自定义实例,赋值给ref.current
- 绑定句柄生成函数与
设计意义
- 静态
Layout标记优化commit遍历性能 :挂载时打上LayoutStaticEffect永久静态标记,通过subtreeFlags冒泡汇总,commit阶段可直接跳过整棵无自定义ref副作用的子树,减少Fiber遍历开销 - 自动内置销毁清理逻辑,防止内存泄漏 :依赖变更 / 组件卸载时,将旧
ref.current置空,避免父ref持有已销毁组件实例,形成内存泄漏
imperativeHandleEffect
执行自定义实例绑定、清理销毁的副作用工厂。区分对象 ref 与回调 ref 两种格式,执行用户工厂函数生成自定义实例并绑定至 ref,针对两种 ref 分别生成标准化销毁逻辑,卸载或依赖变更时自动清空 ref 引用,同时为回调 ref 开放自定义清理回调能力,依托 Layout 副作用机制完成实例绑定与资源释放完整生命周期管理
javascript
function imperativeHandleEffect(
create,
ref,
) {
// 分支1:ref 是回调函数 ref={(inst) => {}}
if (typeof ref === 'function') {
const refCallback = ref;
// 执行用户句柄生成函数,拿到自定义实例
const inst = create();
// 传入实例执行ref回调,捕获回调返回的清理函数
const refCleanup = refCallback(inst);
return () => {
if (typeof refCleanup === 'function') {
refCleanup();
} else {
refCallback(null);
}
};
} else if (ref !== null && ref !== undefined) {
// 分支2:ref 是对象格式 { current }
const refObject = ref;
// 生成自定义实例
const inst = create();
// 挂载到 ref.current
refObject.current = inst;
// 返回销毁函数,清空ref引用
return () => {
refObject.current = null;
};
}
}
作用
- 回调式
ref- 缓存回调
ref,便于后续销毁闭包捕获 - 执行用户传入
useImperativeHandle的第二个参数create()工厂函数,生成对外暴露的自定义实例对象 - 将自定义实例传入父组件传递的
ref回调执行 - 返回副作用销毁闭包(
Effect的destroy)- 如果
refCallback返回了清理函数:优先执行用户自定义清理逻辑 - 无返回清理函数:规范调用
refCallback(null),清空父组件保存的实例引用
- 如果
- 缓存回调
- 对象式
ref- 缓存
ref对象,闭包捕获,销毁时可访问 - 执行用户句柄工厂生成自定义实例
- 将实例挂载到
ref.current,父组件可读取内部暴露的方法 / 属性 - 组件卸载、依赖更新时,将
refObject.current = null,切断ref对组件实例的强引用,防止DOM/ 组件实例无法GC,内存泄漏
- 缓存
设计意义
完整双向生命周期闭环,自动防内存泄漏
- 挂载:创建实例 → 绑定
ref - 销毁 / 重渲染:清空
ref引用
更新阶段:updateImperativeHandle
在组件更新时自动将 ref 并入依赖数组,调用 Layout 副作用更新底层实现依赖对比,依赖变化则预绑定实例创建逻辑生成新 Effect,commit 布局阶段先清理旧 ref 再绑定新自定义实例,依托统一副作用体系实现 useImperativeHandle 更新阶段完整生命周期管理
javascript
function updateImperativeHandle(
ref,
create,
deps,
) {
// 构造包含 ref 的完整依赖数组
const effectDeps =
deps !== null && deps !== undefined ? deps.concat([ref]) : null;
// 调用更新阶段Layout副作用底层实现
updateEffectImpl(
UpdateEffect,
HookLayout,
imperativeHandleEffect.bind(null, create, ref),
effectDeps,
);
}
作用
- 拼接包含 ref 的完整依赖数组
- 调用
updateEffectImpl更新布局副作用- 标识该副作用属于
ref实例更新类Layout副作用;commit阶段依靠LayoutMask快速筛选存在自定义ref的组件子树,做遍历短路优化 - 标记这是同步
Layout副作用(同useLayoutEffect)。依赖变化时,先执行上一轮Effect的destroy清理旧ref,再执行新的imperativeHandleEffect绑定新实例 ,保证旧ref先置空,避免新旧ref同时持有实例 - 生成闭包,捕获本轮最新
ref与创建逻辑,依赖变更时作为新副作用工厂存入Effect - 依赖全部相等:仅更新
Effect缓存,Effect 不携带HookHasEffect标记,commit阶段跳过执行,复用旧ref实例 - 依赖存在变化:生成新
Effect,打上HookHasEffect,先清理旧ref,再执行新逻辑绑定新实例
- 标识该副作用属于
useTransition
挂载阶段:mountTransition
在组件初次挂载时复用 state 底层创建 pending 状态队列,预绑定组件上下文生成永久稳定的 startTransition 函数,创建并串联标准 Hook 节点缓存该函数,最终返回初始未过渡标记与过渡启动函数
javascript
function mountTransition() {
const stateHook = mountStateImpl(false);
const start = startTransition.bind(
null,
// 当前 useTransition 所属组件 WIP Fiber,调度过渡时标记来源组件
currentlyRenderingFiber,
// 控制 isPending 状态的 state 更新队列,过渡开始 / 完成时更新 pending 布尔值
stateHook.queue,
// 标记这是用户层 useTransition创建的过渡
true,
// 无特殊反向回退标记
false,
);
const hook = mountWorkInProgressHook();
hook.memoizedState = start;
return [false, start];
}
作用
- 复用
useState底层状态队列机制,创建一个存储布尔值isPending的状态Hook- 创建标准 State Hook,初始值传入
false,对应useTransition返回的isPending布尔标记 - 内部生成专属
queue更新环形链表,用于过渡开始 / 结束时派发状态更新,切换isPending - 所有过渡
pending状态变更,统一走常规state更新调度,复用Lane优先级、批量更新、并发调度整套逻辑,无需单独实现一套状态管理
- 创建标准 State Hook,初始值传入
- 偏函数绑定:固化
fiber、state队列、过渡标识,生成稳定startTransition。挂载阶段仅bind一次,生成的start函数引用永久不变,多次重渲染不会重建函数,传给子组件、存入依赖数组时引用稳定,不会触发多余重渲染 - 创建
useTransition对应的Hook节点,并入全局Hooks单向链表 - 复用
Hook原生memoizedState字段持久缓存绑定完成的startTransition函数。更新阶段updateTransition直接从hook.memoizedState取出缓存函数,不需要重新bind,保证引用全程不变 - 初始
isPending为false,挂载时无正在执行的过渡任务,返回标准[isPending, startTransition]
startTranstion
切换全局连续过渡调度上下文并同步乐观开启 pending 占位,执行用户过渡回调并自动兼容同步逻辑与异步 Promise 资源,异常兜底保证过渡状态正常收尾,通过 finally 无条件恢复全局调度环境,实现可被交互抢占的并发过渡渲染完整能力
javascript
function startTransition(
fiber,
queue, // 控制 isPending 的 state 更新队列
pendingState,
finishedState,
callback, // 用户传入需要包裹进过渡的更新逻辑
options,
) {
// 快照当前全局更新优先级,提升至连续过渡优先级
const previousPriority = getCurrentUpdatePriority();
setCurrentUpdatePriority(
higherEventPriority(previousPriority, ContinuousEventPriority),
);
// 快照旧全局Transition,新建当前过渡上下文对象,兼容视图/手势过渡特性
const prevTransition = ReactSharedInternals.T;
const currentTransition = {};
// 继承外层过渡的视图动画类型,支持嵌套 transition
if (enableViewTransition) {
currentTransition.types =
prevTransition !== null
? prevTransition.types
: null;
}
// 初始化手势过渡标记
if (enableGestureTransition) {
currentTransition.gesture = null;
}
ReactSharedInternals.T = currentTransition;
// 乐观派发:立即将 isPending 更新为 pendingState(true)
dispatchOptimisticSetState(fiber, false, queue, pendingState);
try {
// 执行用户包裹的过渡回调
const returnValue = callback();
// 执行全局过渡后置钩子 S
const onStartTransitionFinish = ReactSharedInternals.S;
if (onStartTransitionFinish !== null) {
onStartTransitionFinish(currentTransition, returnValue);
}
// 判断回调返回Promise(异步过渡)/普通同步值
if (
returnValue !== null &&
typeof returnValue === 'object' &&
typeof returnValue.then === 'function'
) {
// 返回Promise:链式包装Promise,Promise完成后派发finishedState
const thenable = returnValue;
const thenableForFinishedState = chainThenableValue(
thenable,
finishedState,
);
dispatchSetStateInternal(
fiber,
queue,
thenableForFinishedState,
requestUpdateLane(fiber),
);
} else {
// 同步逻辑:直接派发 finishedState(false)
dispatchSetStateInternal(
fiber,
queue,
finishedState,
requestUpdateLane(fiber),
);
}
} catch (error) {
// 回调同步抛出异常:生成拒绝Promise兜底派发收尾状态
const rejectedThenable = {
then() {},
status: 'rejected',
reason: error,
};
dispatchSetStateInternal(
fiber,
queue,
rejectedThenable,
requestUpdateLane(fiber),
);
} finally {
// 恢复全局调度优先级
setCurrentUpdatePriority(previousPriority);
// 同步回传视图过渡类型到上层Transition上下文
if (prevTransition !== null && currentTransition.types !== null) {
prevTransition.types = currentTransition.types;
}
// 还原全局活跃Transition上下文
ReactSharedInternals.T = prevTransition;
}
}
作用
- 过渡内产生的所有状态更新自动标记为过渡车道,允许同步交互抢占渲染,实现非阻塞 UI
- 快照缓存进入函数前的原有更新优先级,用于
finally恢复 - 切换全局调度优先级为连续过渡优先级。
- 快照缓存进入函数前的原有更新优先级,用于
- 快照旧全局
Transition,新建当前过渡上下文对象,兼容视图/手势过渡特性,压入全局Transition上下文对象ReactSharedInternals.T,渲染阶段所有Lane分配、Suspense纠缠更新会感知本次过渡 - 同步立即派发状态更新,不等待异步调度,将
isPending置为true,页面同步展示加载 / 过渡占位,无需等待异步渲染调度,视觉反馈即时,标记过渡开始 - 执行用户传入过渡回调
callback - 执行全局过渡完成钩子
- 返回
Promise(异步场景:接口请求、Suspense数据加载):适配Suspense、异步数据场景,过渡等待异步资源完成才结束chainThenableValue包装Promise:无论resolve/reject,Promise结束后产出finishedState=false- 将包装后的
thenable作为action派发;内部等待Promise完成再更新isPending
- 普通同步值(纯同步状态更新):直接派发
false,同步逻辑执行完毕立刻标记过渡结束 - 异常捕获兜底同步派发失败状态:构造拒绝态
Thenable推入队列,保证最终一定会派发isPending=false finally无条件恢复全局优先级(核心安全保障)- 还原更新优先级,避免过渡结束后全局持续使用低优先级
- 支持嵌套
startTransition:内层过渡的视图types同步回外层上下文 - 把全局活跃
T恢复为进入函数前的值,避免全局永久标记处于过渡,污染后续普通渲染
设计意义
- 全局上下文快照 +
finally恢复,异常安全 :所有全局可变环境(更新优先级、Transition上下文)执行前快照,finally强制还原;即便用户回调崩溃、Promise报错,不会破坏全局调度环境,后续渲染逻辑不受连锁故障影响 - 乐观 UI 反馈,分层状态更新
- 先同步派发
pending=true,立刻更新视图 - 业务逻辑与收尾
false异步排队;实现 "先占位、后更新内容" 流畅过渡体验,无视觉延迟
- 先同步派发
- 嵌套
Transition上下文透传 :通过prevTransition.types继承与回写,支持多层嵌套startTransition,视图过渡动画上下文自动穿透嵌套层级,扩展能力完备 - 优先级兼容不降级交互 :使用
higherEventPriority取更高优先级:如果在点击 / 输入等高优交互内调用startTransition,不会降低原有交互优先级,保证交互即时响应,仅新增的过渡更新可被抢占
dispatchOptimisticSetState
仅在 startTransition 内部调用,用于同步立刻把 isPending 置为 true,快速展示过渡占位 UI;区别于普通 dispatchSetState,它固定使用同步车道 SyncLane(手势过渡除外),不做 eager 预计算、不做等值短路丢弃更新,强制入队立刻调度,保证页面同步出现加载态,同时兼容手势过渡扩展并同步回滚车道
javascript
function dispatchOptimisticSetState(
fiber,
throwIfDuringRender, // 布尔标记:渲染阶段调用是否直接抛错
queue,
action,
) {
// 读取全局活跃 Transition 上下文 ReactSharedInternals.T
const transition = requestCurrentTransition();
// 根据是否手势过渡分配车道:手势=GestureLane,其余固定SyncLane
const lane =
enableGestureTransition && transition !== null && transition.gesture
? GestureLane // 手势过渡专属优先级车道
: SyncLane;
// 构建乐观更新专用Update节点
const update = {
// 上一步分配的同步 / 手势车道
lane: lane,
// 生成当前过渡对应的回滚 revertLane,用于 Suspense 挂起时用于撤销本次更新
revertLane: requestTransitionLane(transition),
gesture: null,
// 本次要更新的值(true 开启 pending)
action,
// 禁用 eager 同步预计算
// 关键区别普通 dispatchSetState:乐观更新不做预计算、不做等值短路,无论新旧值是否相等,强制入队执行渲染,保证占位 UI 一定出现
hasEagerState: false,
eagerState: null,
next: null,
};
// 判断是否在渲染阶段调用
if (isRenderPhaseUpdate(fiber)) {
// 渲染内调用且开启抛错标记,直接抛出非法更新错误
if (throwIfDuringRender) {
throw new Error('Cannot update optimistic state while rendering.');
}
} else {
// 将 Update 推入 Hook 环形 pending 队列,向上收集 Fiber、返回根节点
const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
if (root !== null) {
// 全局渲染调度入口
scheduleUpdateOnFiber(root, fiber, lane);
// 手势过渡分支:绑定手势任务,同步回滚revertLane
if (enableGestureTransition && transition !== null) {
const provider = transition.gesture;
if (provider !== null) {
// 手势过渡调度工具,绑定手势任务与回滚车道
const gesture = (update.gesture = scheduleGesture(root, provider));
if (gesture.revertLane === NoLane) {
gesture.revertLane = update.revertLane;
} else {
update.revertLane = gesture.revertLane;
}
}
}
}
}
}
作用
- 读取全局当前正在执行的
Transition上下文对象 - 分配更新优先级
lane:乐观更新是展示加载占位 UI,必须优先同步渲染,不能被低优任务抢占,所以默认强制同步车道;手势交互单独使用专属车道,调度器对手势做特殊连续流畅处理- 开启手势特性、存在活跃手势过渡 → 使用
GestureLane手势专属车道 - 其余普通
useTransition乐观更新固定使用SyncLane同步最高优先级
- 开启手势特性、存在活跃手势过渡 → 使用
- 构造乐观更新专用
Update对象(禁用eager预计算):乐观更新语义优先级高于性能优化,禁止跳过更新,牺牲eager优化换取视觉即时反馈 - 渲染阶段调用安全校验分支:渲染阶段触发乐观更新 → 跳过入队、调度逻辑,不产生渲染。约束乐观更新只能在事件、异步回调等非渲染上下文执行,防止渲染循环无限触发
pending状态更新 - 非渲染阶段:入队 + 调度主逻辑:同步
SyncLane会立即执行渲染,保证isPending=true马上生效,页面快速展示过渡占位- 将新建
Update推入queue.pending环形更新链表 - 自底向上遍历父
Fiber,沿途合并lane标记 - 返回当前组件所属
FiberRoot,组件已卸载则返回null - 校验:组件未卸载才执行调度,标记根待更新车道,交由
Scheduler安排同步渲染 - 手势过渡扩展分支
scheduleGesture创建手势调度实例绑定到update.gesture- 双向同步
revertLane回滚车道:手势实例与Update共享同一个回滚优先级
- 将新建
设计意义
- 乐观更新语义优先,禁用
eager短路优化 :普通dispatchSetState会在新旧值相等时直接删除Update、取消渲染;乐观更新为了视觉反馈一致性,关闭eager预计算与等值短路,强制触发渲染,保证加载占位一定展示 - 默认同步最高优先级,保障即时视觉反馈 :默认使用
SyncLane同步车道,调度器会立即执行渲染,不会被低优并发任务延迟,实现 "点击立刻出占位" 的流畅过渡体验;手势场景单独分配GestureLane做连续交互优化
更新阶段
javascript
function updateTransition() {
// 1. 更新阶段读取控制pending状态的state(boolean / thenable)
const [booleanOrThenable] = updateState(false);
// 2. 匹配WIP Hook,取出挂载阶段缓存的startTransition函数
const hook = updateWorkInProgressHook();
const start = hook.memoizedState;
// 3. 判断state是布尔值还是异步thenable,计算最终isPending标记
const isPending =
typeof booleanOrThenable === 'boolean'
? booleanOrThenable
: useThenable(booleanOrThenable);
// 4. 返回标准 [pending标记, 稳定startTransition]
return [isPending, start];
}
作用
- 调用更新阶段通用
state底层updateState,读取useTransition内部配套的状态队列最新值 - 同步推进
currentHook/workInProgressHook双游标,克隆旧Hook生成WIP副本 - 取出挂载阶段缓存的
startTransition函数 - 分支判断计算
isPending- 值为布尔
true/false(同步过渡):直接把布尔值作为isPending - 值为
thenable Promise(异步Suspense过渡):内部会读取异步资源状态,若Promise未resolve则返回true(页面保持pending加载态);Promise完成后返回false,关闭过渡占位。同时自动触发Suspense挂起逻辑,渲染fallback
- 值为布尔
- 返回标准化
[isPending, startTransition]供组件使用
设计意义
startTransition 全程引用稳定,零额外内存分配 :仅挂载阶段执行一次 bind 绑定上下文,所有更新渲染直接从 Hook.memoizedState 复用;不会每次渲染生成新函数
重渲染:rerenderTransition
仅在渲染阶段内部同步调用 startTransition /setState 触发循环重渲染时执行
javascript
function rerenderTransition() {
// 1. 读取渲染阶段缓存的 state(布尔 / thenable)
const [booleanOrThenable] = rerenderState(false);
// 2. 匹配WIP Hook,取出挂载阶段缓存稳定的 startTransition
const hook = updateWorkInProgressHook();
const start = hook.memoizedState;
// 3. 区分同步布尔 / 异步Promise,计算当前 isPending
const isPending =
typeof booleanOrThenable === 'boolean'
? booleanOrThenable
: useThenable(booleanOrThenable);
// 4. 统一返回标准 [isPending, startTransition]
return [isPending, start];
}
作用
与 updateTransition 几乎一致,唯一区别:读取状态使用 rerenderState(渲染阶段临时更新队列读取),其余逻辑完全复用
useDeferredValue
挂载阶段:mountDeferredValue
通过统一工具创建并串联标准 Hook 链表节点,将 Hook 实例、原始值与初始值透传给底层调度实现函数,最终返回延迟快照值
javascript
function mountDeferredValue(value, initialValue) {
// 1. 创建Hook节点,并入组件全局Hooks单向链表
const hook = mountWorkInProgressHook();
// 2. 将Hook、原始传入值、初始值交给底层实现处理,返回延迟值
return mountDeferredValueImpl(hook, value, initialValue);
}
作用
- 复用全局统一
mountWorkInProgressHook创建Hook链表节点,完成标准化挂载 - 把新建的
Hook实例、用户传入原始值、初始值透传给底层实现函数 - 透传返回底层生成的延迟快照值,对外提供
useDeferredValue能力
mountDeferredValueImpl
在组件初次挂载时判断是否启用初始占位逻辑,存在合法 initialValue 且非延迟渲染上下文时,缓存占位快照、申请可抢占低优延迟车道并挂载到当前 Fiber,页面先渲染初始占位值;无初始值或嵌套延迟渲染时直接缓存并返回实时原值,依托 React Lane 并发调度实现 useDeferredValue 初始占位与后台异步更新能力
javascript
function mountDeferredValueImpl(hook, value, initialValue) {
// 1. 判断是否启用初始占位逻辑:传了initialValue + 当前不在延迟渲染任务中
if (
initialValue !== undefined &&
!isRenderingDeferredWork()
) {
// 2. Hook持久缓存初始占位值
hook.memoizedState = initialValue;
// 3. 获取延迟更新专属低优先级车道
const deferredLane = requestDeferredLane();
// 4. 将延迟车道合并到当前渲染Fiber的待更新车道集合
currentlyRenderingFiber.lanes = mergeLanes(
currentlyRenderingFiber.lanes,
deferredLane,
);
// 5. 标记该车道为可跳过更新车道(允许高优交互抢占)
markSkippedUpdateLanes(deferredLane);
// 6. 页面先渲染初始占位值
return initialValue;
} else {
// 7. 不启用初始占位:缓存并直接返回实时传入value
hook.memoizedState = value;
return value;
}
}
作用
- 是否开启初始占位延迟逻辑:传了
initialValue且当前不在延迟渲染任务中 - 满足上述条件
- 把初始占位值存入
Hook的持久存储memoizedState,作为本轮渲染输出值,同时作为下一轮更新对比基准。useDeferredValue依靠memoizedState保存上一轮稳定延迟快照值 - 申请延迟更新专属低优先级车道 (
ContinuousEvent连续过渡车道)- 可被离散交互(点击、输入)高优任务抢占
- 浏览器空闲时才执行,不会阻塞输入响应
- 按位合并两套优先级车道,将延迟车道加入组件待更新标记,调度器会识别该组件存在低优延迟任务,主线程空闲时重新渲染组件,把快照值更新为最新
value - 标记这条延迟车道为可被跳过的更新车道:当有点击、输入等高优交互到来时,调度器可以直接丢弃未完成的延迟渲染,优先执行交互更新,保证输入流畅,不会被延迟渲染阻塞
- 初次挂载页面直接渲染初始占位值,真实
value会在后续空闲时机低优更新进来
- 把初始占位值存入
- 无初始占位场景
- 直接缓存实时最新
value - 页面直接展示最新传入的值,无占位延迟效果
- 直接缓存实时最新
设计意义
交互优先的抢占式设计 :markSkippedUpdateLanes 标记延迟任务可丢弃,输入、点击等高优操作到来时直接打断延迟渲染,优先响应用户交互,解决大数据渲染卡顿输入的痛点
更新阶段:updateDeferredValue
通过统一工具匹配克隆新旧 Hook 并快照锁定旧 Hook 读取上一轮稳定延迟快照,将全部上下文参数透传给底层延迟更新实现函数,最终返回计算后的延迟快照
javascript
function updateDeferredValue(value, initialValue) {
// 1. 匹配WIP Hook,同步推进新旧Hook游标,克隆旧Hook生成当前渲染副本
const hook = updateWorkInProgressHook();
// 2. 缓存当前渲染对应的旧Hook(currentHook是全局游标,临时快照防止被覆盖)
const resolvedCurrentHook = currentHook;
// 3. 取出上一轮持久缓存的稳定延迟快照值
const prevValue = resolvedCurrentHook.memoizedState;
// 4. 将所有上下文透传给底层实现,返回本轮最终延迟快照值
return updateDeferredValueImpl(hook, prevValue, value, initialValue);
}
作用
- 通过统一工具匹配克隆新旧
Hook,维护组件全局单向Hook链表 - 快照锁定旧
Hook,读取上一轮持久缓存的延迟基准快照 - 将
WIP Hook、旧快照、实时新值、初始占位值全部透传给底层更新实现 - 透传底层返回的稳定延迟快照,对外输出
useDeferredValue结果
updateDeferredValueImpl
在组件更新时先全等短路复用旧快照,隐藏子树则复用挂载逻辑重建快照;再根据当前渲染车道优先级与执行上下文判断是否开启延迟,开启时申请可抢占低优延迟车道并返回旧快照,无延迟则同步更新 Hook 缓存并返回最新实时值,依托 React Lane 并发调度实现交互优先的延迟值渲染策略
javascript
function updateDeferredValueImpl(
hook,
prevValue,
value,
initialValue,
) {
// 1. 新旧值完全相等,直接返回旧快照,无任何更新、无延迟调度
if (is(value, prevValue)) {
return value;
} else {
// 2. 当前渲染树处于隐藏/离屏状态,复用挂载逻辑重新计算延迟快照
if (isCurrentTreeHidden()) {
const resultValue = mountDeferredValueImpl(hook, value, initialValue);
// 快照发生变化,标记WIP存在更新
if (!is(resultValue, prevValue)) {
markWorkInProgressReceivedUpdate();
}
return resultValue;
}
// 3. 判断是否应当开启延迟逻辑
const shouldDeferValue =
!includesOnlyNonUrgentLanes(renderLanes) && !isRenderingDeferredWork();
if (shouldDeferValue) {
// 4. 开启延迟:申请低优延迟车道、合并Fiber标记、标记可抢占
const deferredLane = requestDeferredLane();
currentlyRenderingFiber.lanes = mergeLanes(
currentlyRenderingFiber.lanes,
deferredLane,
);
markSkippedUpdateLanes(deferredLane);
// 返回旧快照,页面继续展示旧值,后台空闲时更新
return prevValue;
} else {
// 5. 不开启延迟:标记组件更新、写入最新值缓存、返回实时新值
markWorkInProgressReceivedUpdate();
hook.memoizedState = value;
return value;
}
}
}
作用
- 本轮新值与上一轮稳定延迟快照完全一致,无需任何更新、不产生延迟车道,直接复用旧快照返回
- 判断当前组件所在子树被标记为隐藏(
Suspense fallback、display:none、离屏缓存等),离屏 / 挂起组件不需要维持延迟更新的连续快照逻辑- 直接复用挂载阶段
mountDeferredValueImpl重新计算快照,统一处理initialValue占位逻辑 - 若新计算出的快照和上一轮
prevValue不一致,调用markWorkInProgressReceivedUpdate标记当前WIP组件产生了状态变更,调和阶段不会被 bailout 跳过 - 返回重新计算后的快照值
- 直接复用挂载阶段
- 计算是否需要延迟
shouldDeferValue,两个条件同时成立才开启延迟- 当前渲染包含高优先级车道(输入、点击等交互更新),交互优先级高于大数据渲染,需要延迟非紧急的值更新
- 当前不在低优延迟渲染上下文,防止延迟嵌套无限循环渲染
- 开启延迟分支
- 申请连续低优先级延迟车道,可被交互抢占
- 把延迟车道按位合并到当前组件
Fiber的待更新车道集合 - 标记该延迟车道可被丢弃,高优交互到来时直接中断延迟渲染,保证输入流畅
- 本轮页面继续展示旧快照值 ,调度器在主线程空闲时发起低优重渲染,再更新为最新
value
- 不开启延迟分支(同步更新)
markWorkInProgressReceivedUpdate标记组件存在变更,禁止调和bailout跳过- 将最新实时值存入
hook.memoizedState作为新稳定快照 - 直接返回最新
value,页面同步展示新值,不产生后台延迟任务
设计意义
基于 Lane 优先级的动态延迟策略 :延迟不是固定开启,而是动态判断。只有存在高优交互任务时才延后非紧急值,纯低优渲染场景直接同步更新,兼顾流畅交互与普通渲染性能
重渲染阶段:rerenderDeferredValue
用于渲染阶段内部产生更新、触发 renderWithHooksAgain 循环重渲染 场景(render 函数体内同步触发状态更新)。通过统一工具匹配克隆 Hook,无旧 Hook 时复用挂载初始化内核,存在历史缓存则读取上一轮延迟快照并复用更新阶段完整延迟调度逻辑,保证循环重渲染场景下延迟值行为与普通更新、初次挂载完全一致
javascript
function rerenderDeferredValue(value, initialValue) {
// 1. 匹配WIP链表中当前位置的Hook,推进新旧Hook游标
const hook = updateWorkInProgressHook();
// 2. 安全分支:无对应旧Hook(极罕见边界)走挂载初始化逻辑
if (currentHook === null) {
return mountDeferredValueImpl(hook, value, initialValue);
} else {
// 3. 存在旧Hook,读取上一轮缓存的延迟快照
const prevValue = currentHook.memoizedState;
// 4. 复用更新阶段核心调度逻辑计算本轮延迟值
return updateDeferredValueImpl(hook, prevValue, value, initialValue);
}
}
作用
- 通过统一工具匹配克隆
Hook,维护组件单向Hook链表 - 兜底处理无旧
Hook的边界异常,复用挂载初始化逻辑 - 常规场景读取上一轮缓存快照,复用更新阶段完整延迟调度内核
- 对外输出与挂载、正常更新完全一致的延迟快照,上层业务无感知底层渲染场景差异