数据结构
typescript
type Hook = {
memoizedState: any, // 当前状态值
// 并发渲染中断恢复的基准状态,渲染被抢占重启时,从 baseState 重新合并更新队列
// 本轮计算的起点状态
baseState: any,
// 未被当前渲染车道消费、需要留到下一轮的 Update 环形链表
// 保存上一轮未完全处理完的更新队列,并发中断场景用于恢复更新
baseQueue: Update | null,
// useState /useReducer 专属更新队列,存放 setState /dispatch 产生的 Update、优先级 lanes、dispatch 函数
queue: UpdateQueue | null,
next: Hook | null, // 下一个 hook(单向链表)
};
type Update<S, A> = {
lane: Lane, // 本次更新优先级 lane
revertLane: Lane, // 回滚优先级(Suspense 异步回退)
action: A, // 更新的值或 reducer 函数
// 同步预计算的即时状态(eager 更新优化)
hasEagerState: boolean, // 是否已预计算
eagerState: S | null, // 预计算的状态
next: Update<S, A>, // 下一个 update(循环链表)
// 手势过渡专属标记
gesture: null | ScheduledGesture,
};
type UpdateQueue<S, A> = {
// 本轮渲染产生、未合并的环形 Update 链表
// 指向循环链表最后一个 update,存储所有 setState 产生的待处理更新对象 Update
pending: Update<S, A> | null,
lanes: Lanes, // 队列中所有 update 的合并 lane
dispatch: (A => mixed) | null, // setState 函数引用
lastRenderedReducer: ((S, A) => S) | null, // 上次渲染的 reducer
lastRenderedState: S | null, // 缓存上一轮渲染完成后的最终 state
};
Hook单向链表:单个useState对应的链表节点,存放memoizedState(当前状态值)、queue(更新队列)。fiber.memoizedState→ Hook1 → Hook2 → Hook3,按调用顺序串联Update循环链表:State更新队列对象,存储所有待执行更新、dispatch函数。UpdateQueue.pending指向最后一个update,pending.next是第一个updatedispatchSetState:底层通用派发更新逻辑,接收fiber、队列、新状态值,入队更新并触发调度baseState/baseQueue:用于低优先级更新回退 ,跳过的update保留在baseQueue,baseState是跳过的前一个状态
挂载:mountState
javascript
function mountState(initialState) {
const hook = mountStateImpl(initialState); // ① 创建 hook
const queue = hook.queue; // ② 获取队列
const dispatch = dispatchSetState.bind( // ③ 绑定 setState
null,
currentlyRenderingFiber,
queue,
);
queue.dispatch = dispatch; // ④ 存储 dispatch 引用
return [hook.memoizedState, dispatch]; // ⑤ 返回 [state, setState]
}
function mountReducer(
reducer,
initialArg,
init,
) {
const hook = mountWorkInProgressHook();
let initialState;
if (init !== undefined) {
initialState = init(initialArg);
} else {
initialState = initialArg;
}
hook.memoizedState = hook.baseState = initialState;
const queue = {
pending: null,
lanes: NoLanes,
dispatch: null,
lastRenderedReducer: reducer,
lastRenderedState: (initialState: any),
};
hook.queue = queue;
const dispatch = queue.dispatch = dispatchReducerAction.bind(
null,
currentlyRenderingFiber,
queue,
);
return [hook.memoizedState, dispatch];
}
作用
- 创建、初始化
hook节点,计算初始状态,返回完整初始化完毕的Hook实例,包含状态值、空更新队列、链表指针 - 从刚创建的
Hook上取出专属更新队列,后续所有setState产生的更新都会存在这个队列里。每个useState拥有独立queue,互不干扰。 - 生成稳定的
setState派发函数,每个useState的dispatch是独立函数实例 - 存储
dispatch引用,把生成好的setState函数挂载到更新队列queue.dispatch上。后续更新阶段(updateState)读取队列时,直接复用同一个dispatch引用,保证setState引用稳定(不重复创建函数,避免memo组件无效重渲染) - 返回
[state, setState],当前稳定的状态值(初始值)+ 绑定好fiber和队列的setState函数
Hook 节点构造器:mountStateImpl
javascript
function mountStateImpl(initialState) {
const hook = mountWorkInProgressHook();
if (typeof initialState === 'function') {
const initialStateInitializer = initialState;
initialState = initialStateInitializer(); // 惰性初始化:() => initial
}
hook.memoizedState = hook.baseState = initialState;
const queue = {
pending: null, // 无等待 update
lanes: NoLanes, // 无 lane
dispatch: null, // 稍后设置
// useState 本质是 useReducer 的特化,reducer 固定为 basicStateReducer
lastRenderedReducer: basicStateReducer, // 固定为 basicStateReducer
lastRenderedState: initialState, // 记录初始状态
};
hook.queue = queue;
return hook;
}
function basicStateReducer(state, action) {
return typeof action === 'function' ? action(state) : action;
}
作用
- 创建
WIP Hook节点,插入Hooks单向链表,为本轮useState生成独立链表节点,自动串联到当前组件的Hooks链表尾部,保证Hooks调用顺序可复现。所有Hooks(useState/useEffect/useContext)共用同一套链表串联逻辑,统一由mountWorkInProgressHook维护 - 惰性初始化逻辑:如果
initialState是函数,执行取值,计算真实初始state。仅挂载阶段执行一次,更新阶段不会重复执行 - 同步赋值基础状态与当前渲染状态
memoizedState:本次渲染直接读取的最新稳定state,组件渲染时const [state] = useState()拿到的值baseState:基础基准状态,用于并发渲染中断恢复 。并发渲染被高优任务打断、中途重启渲染时,会以baseState为起点重新应用未处理的更新队列,保证状态计算正确性
- 初始化
useState专属更新队列queue对象 - 返回完整初始化后的
Hook实例
Hook 链表构造器:mountWorkInProgressHook
javascript
function mountWorkInProgressHook() {
const hook = {
memoizedState: null,
baseState: null,
baseQueue: null,
queue: null,
next: null,
};
if (workInProgressHook === null) {
// 第一个 hook → 挂在 fiber 上
currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
} else {
// 后续 hook → 追加到链表尾部
workInProgressHook = workInProgressHook.next = hook;
}
return workInProgressHook;
}
作用
- 新建空白
Hook节点,初始化全部内置字段 - 维护组件
Hooks单向链表:区分首节点 / 后续节点,自动挂载到Fiber尾部 - 维护全局链表尾游标
workInProgressHook,实现链式追加 - 返回刚创建、挂载完成的
Hook节点
更新:updateState
updateReducer 是更新阶段 useReducer 的顶层分发函数,先通过通用 Hook 克隆工具匹配新旧 Hooks 链表,再将 Hook 相关上下文转发至底层实现完成状态更新计算,分层隔离链表操作与状态演算逻辑,同时保持与挂载阶段一致的函数接口
javascript
function updateState(initialState) {
// ← 委托给 updateReducer
return updateReducer(basicStateReducer, initialState);
}
function updateReducer(
reducer,
initialArg,
init,
) {
const hook = updateWorkInProgressHook(); // ① 获取/克隆当前 hook
return updateReducerImpl(hook, currentHook, reducer); // ② 计算新状态
}
作用
- 获取/克隆当前
hook:拿到当前useReducer对应的、从旧Hook复制而来的WIP Hook节点,为后续处理更新队列做载体。更新阶段不会凭空新建Hook,而是复用挂载阶段的Hook结构 ,保证新旧Hooks链表顺序一一对应 - 将
WIP Hook、旧Hook、用户reducer透传给底层updateReducerImpl执行状态合并计算,处理更新队列、状态合并、reducer对比 - 直接转发底层返回的
[state, dispatch]作为useReducer的返回值
设计意义
useState 和 useReducer 在 update 阶段共享同一套更新计算逻辑。区别只在于 mount 阶段 mountState 使用 basicStateReducer,而 mountReducer 使用用户传入的自定义 reducer
Hook 链表更新器:updateWorkInProgressHook
updateWorkInProgressHook 在组件更新阶段同步遍历新旧两条 Hooks 单向链表,优先复用已存在的 WIP Hook,无匹配节点时从旧 Hook 克隆生成新 WIP 链表节点,同时校验新旧 Hook 数量一致性并抛出 Hooks 规则报错
ini
function updateWorkInProgressHook() {
let nextCurrentHook;
if (currentHook === null) {
const current = currentlyRenderingFiber.alternate;
if (current !== null) {
nextCurrentHook = current.memoizedState;
} else {
nextCurrentHook = null;
}
} else {
nextCurrentHook = currentHook.next;
}
let nextWorkInProgressHook;
if (workInProgressHook === null) {
nextWorkInProgressHook = currentlyRenderingFiber.memoizedState;
} else {
nextWorkInProgressHook = workInProgressHook.next;
}
if (nextWorkInProgressHook !== null) {
workInProgressHook = nextWorkInProgressHook;
nextWorkInProgressHook = workInProgressHook.next;
currentHook = nextCurrentHook;
} else {
if (nextCurrentHook === null) {
const currentFiber = currentlyRenderingFiber.alternate;
if (currentFiber === null) {
throw new Error(
'Update hook called on initial render. This is likely a bug in React. Please file an issue.',
);
} else {
throw new Error('Rendered more hooks than during the previous render.');
}
}
currentHook = nextCurrentHook;
const newHook = {
memoizedState: currentHook.memoizedState,
baseState: currentHook.baseState,
baseQueue: currentHook.baseQueue,
queue: currentHook.queue,
next: null,
};
if (workInProgressHook === null) {
currentlyRenderingFiber.memoizedState = workInProgressHook = newHook;
} else {
workInProgressHook = workInProgressHook.next = newHook;
}
}
return workInProgressHook;
}
作用
- 计算旧链表下一个待匹配
Hook nextCurrentHook - 计算WIP链表下一个待匹配
Hook nextWorkInProgressHook WIP链表已经提前存在对应Hook,直接复用。并发渲染被中断、重启调和时,WIP链表已经提前生成一部分Hook,不需要重复克隆,直接复用已有节点,提升性能- 全局
WIP游标更新为当前匹配到的WIP Hook - 预存下一轮
WIP候选(预前移) - 同步把旧链表游标移动到对应旧
Hook
- 全局
WIP无对应Hook,需要从旧Hook克隆新建- 旧链表已经没有
Hook,但代码还在执行新Hook→ 多写Hook报错 - 同步旧游标到下一个
Hook,把全局旧Hook游标指向本轮要克隆的旧Hook - 完整克隆旧
currentHook所有字段,生成新WIP Hook。浅拷贝旧Hook全部核心状态字段,memoizedState/baseState/baseQueue/queue全部复用旧引用 - 将克隆的
newHook挂载到WIP Hooks链表尾部
- 旧链表已经没有
- 返回当前匹配完成的
WIP Hook,供useState/useReducer等处理业务逻辑
设计意义
- 新旧双链表同步匹配 :通过同步推进
currentHook/workInProgressHook双游标,强制新旧Hook一一对应;链表长度不一致直接抛错 - 兼容并发渲染中断恢复 :完整拷贝
baseState/baseQueue并发相关字段,克隆后的WIP Hook天然支持渲染被抢占后重启、恢复未完成更新队列,底层预埋并发能力
Hook 状态计算引擎:updateReducerImpl
通过环形链表合并多批次更新,基于渲染优先级车道筛选执行 / 留存 Update,兼容 Suspense 回滚、手势、隐藏组件并发场景,迭代计算最新 state 并构建可用于中断恢复的基准更新队列,最终返回稳定 state 与派发函数
ini
function updateReducerImpl(hook, current, reducer) {
// 1. 取出当前Hook专属更新队列
const queue = hook.queue;
// 2. 防御校验:不存在队列=Hook调用顺序错乱
if (queue === null) {
throw new Error(
'Should have a queue. You are likely calling Hooks conditionally, ' +
'which is not allowed. (https://react.dev/link/invalid-hook-call)',
);
}
// 3. 记录本轮使用的reducer,用于对比是否变更
queue.lastRenderedReducer = reducer;
// 4. 取出上一轮遗留的基准更新环形链表
let baseQueue = hook.baseQueue;
// 5. 取出本次渲染新增的待处理更新环形链表
const pendingQueue = queue.pending;
if (pendingQueue !== null) {
// 5.1 旧有基准队列存在:合并两个环形链表
if (baseQueue !== null) {
const baseFirst = baseQueue.next;
const pendingFirst = pendingQueue.next;
baseQueue.next = pendingFirst;
pendingQueue.next = baseFirst;
}
// 5.2 合并完成后,将合并后的链表存入旧Hook持久化,并赋值给baseQueue
current.baseQueue = baseQueue = pendingQueue;
// 清空本轮临时pending,避免重复处理
queue.pending = null;
}
// 6. 基准计算起点:上一轮遗留基准状态
const baseState = hook.baseState;
if (baseQueue === null) {
// 无任何待处理更新,state直接复用基准state
hook.memoizedState = baseState;
} else {
// 存在待处理Update,开始循环遍历所有更新
const first = baseQueue.next;
let newState = baseState;
// 用于构建本轮结束后新的baseQueue(未消费的更新)
// 遍历过程中持续累积计算的中间状态
let newBaseState = null;
// 构建本轮结束后新的基准环形链表
let newBaseQueueFirst = null;
let newBaseQueueLast = null;
let update = first;
// 标记本轮是否读取到纠缠异步Suspense资源,需要抛 thenable
let didReadFromEntangledAsyncAction = false;
// 环形链表循环遍历所有Update
do {
// 移除Offscreen隐藏车道,得到真实有效优先级
const updateLane = removeLanes(update.lane, OffscreenLane);
// 是否是被Offscreen组件隐藏的更新
const isHiddenUpdate = updateLane !== update.lane;
// 判断:当前渲染车道是否包含该更新的lane,不包含则本轮需要跳过
let shouldSkipUpdate = isHiddenUpdate
? !isSubsetOfLanes(getWorkInProgressRootRenderLanes(), updateLane)
: !isSubsetOfLanes(renderLanes, updateLane);
// 手势过渡分支:GestureLane特殊逻辑
if (enableGestureTransition && updateLane === GestureLane) {
const scheduledGesture = update.gesture;
if (scheduledGesture !== null) {
// 手势已完成计数为0且不在提交阶段,直接丢弃该更新
if (scheduledGesture.count === 0 && !scheduledGesture.committing) {
update = update.next;
continue;
} else if (!isGestureRender(renderLanes)) {
// 当前不是手势渲染车道,跳过
shouldSkipUpdate = true;
} else {
const root = getWorkInProgressRoot();
if (root === null) {
throw new Error(
'Expected a work-in-progress root. This is a bug in React. Please file an issue.',
);
}
// 手势任务不匹配,跳过
shouldSkipUpdate = root.pendingGestures !== scheduledGesture;
}
}
}
if (shouldSkipUpdate) {
// 分支A:本轮车道不匹配,跳过该Update,克隆存入新baseQueue留到下一轮
const clone = {
lane: updateLane,
revertLane: update.revertLane,
gesture: update.gesture,
action: update.action,
hasEagerState: update.hasEagerState,
eagerState: update.eagerState,
next: null,
};
// 构建新baseQueue环形链表
if (newBaseQueueLast === null) {
newBaseQueueFirst = newBaseQueueLast = clone;
newBaseState = newState;
} else {
newBaseQueueLast = newBaseQueueLast.next = clone;
}
// 将跳过的更新车道合并到当前Fiber,标记本Fiber还有未处理更新
currentlyRenderingFiber.lanes = mergeLanes(currentlyRenderingFiber.lanes, updateLane);
// 全局标记存在被跳过的更新,调度器后续会重新调度
markSkippedUpdateLanes(updateLane);
} else {
// 分支B:当前渲染车道匹配,执行该Update;处理revertLane回滚逻辑
const revertLane = update.revertLane;
if (revertLane === NoLane) {
// 无回滚车道,正常执行更新
if (newBaseQueueLast !== null) {
// 已存在留存队列:克隆一份空lane节点占位(仅保留action,不再占用优先级)
const clone = {
lane: NoLane,
revertLane: NoLane,
gesture: null,
action: update.action,
hasEagerState: update.hasEagerState,
eagerState: update.eagerState,
next: null,
};
newBaseQueueLast = newBaseQueueLast.next = clone;
}
// 标记读取到纠缠异步Suspense资源
if (updateLane === peekEntangledActionLane()) {
didReadFromEntangledAsyncAction = true;
}
} else {
// 存在Suspense回滚优先级revertLane
if (isSubsetOfLanes(renderLanes, revertLane)) {
update = update.next;
if (revertLane === peekEntangledActionLane()) {
didReadFromEntangledAsyncAction = true;
}
continue;
} else {
// 无回滚车道匹配:克隆存入baseQueue,标记回滚车道待后续处理
const clone = {
lane: NoLane,
revertLane: update.revertLane,
gesture: null,
action: update.action,
hasEagerState: update.hasEagerState,
eagerState: update.eagerState,
next: null,
};
if (newBaseQueueLast === null) {
newBaseQueueFirst = newBaseQueueLast = clone;
newBaseState = newState;
} else {
newBaseQueueLast = newBaseQueueLast.next = clone;
}
// 合并回滚车道到Fiber,标记存在待回滚更新
currentlyRenderingFiber.lanes = mergeLanes(currentlyRenderingFiber.lanes, revertLane);
markSkippedUpdateLanes(revertLane);
}
}
// 执行更新,计算新state
const action = update.action;
if (update.hasEagerState) {
// 存在同步预计算的eagerState,直接复用,无需重新跑reducer
newState = update.eagerState;
} else {
// 调用用户reducer,基于当前newState计算下一状态
newState = reducer(newState, action);
}
}
// 环形链表前进到下一个Update
update = update.next;
} while (update !== null && update !== first);
// 遍历完成,组装新的环形baseQueue
if (newBaseQueueLast === null) {
// 无任何需要留存的更新,基准state为本次最终state
newBaseState = newState;
} else {
// 收尾环形链表:尾节点指向头节点
newBaseQueueLast.next = newBaseQueueFirst;
}
// 判断状态是否发生变化
if (!is(newState, hook.memoizedState)) {
// state变化,标记当前WIP Fiber需要更新(didReceiveUpdate = true)
markWorkInProgressReceivedUpdate();
// 如果本轮读取到纠缠异步Suspense资源,抛出thenable触发Suspense兜底
if (didReadFromEntangledAsyncAction) {
const entangledActionThenable = peekEntangledActionThenable();
if (entangledActionThenable !== null) {
throw entangledActionThenable;
}
}
}
// 覆盖当前Hook的渲染状态、并发基准状态、基准更新链表
hook.memoizedState = newState;
hook.baseState = newBaseState;
hook.baseQueue = newBaseQueueLast;
// 缓存本轮最终state到队列,供下一轮对比reducer变更
queue.lastRenderedState = newState;
}
// 本轮无任何待处理更新,清空队列优先级标记
if (baseQueue === null) {
queue.lanes = NoLanes;
}
// 取出稳定dispatch函数返回
const dispatch = queue.dispatch;
return [hook.memoizedState, dispatch];
}
作用
- 合并本轮临时
pending更新到持久环形baseQueue - 无任何待处理更新时,直接复用基准
state,跳过昂贵循环计算,性能短路优化 - 以
baseState为起点遍历全部Update,根据当前渲染车道区分跳过 / 执行更新 - 处理
Offscreen隐藏、Gesture手势、Suspense回滚三大并发扩展能力- 计算是否跳过当前
Update(核心车道匹配逻辑):剔除Offscreen隐藏组件专用车道,隐藏组件更新不在当前渲染周期执行 GestureLane手势过渡特殊分支- 手势已结束且未提交:直接丢弃 Update
- 当前渲染不是手势车道:跳过
- 排队手势和当前渲染手势不匹配:跳过
- 计算是否跳过当前
- 跳过的
Update克隆留存到新baseQueue,供下一轮对应优先级渲染消费- 完整克隆当前
Update节点,lane保留原优先级 - 追加到
newBaseQueue环形链表尾部 - 合并
updateLane到currentlyRenderingFiber.lanes,标记当前Fiber存在未处理更新 markSkippedUpdateLanes全局登记被跳过的车道,调度器后续会重新调度该根渲染
- 完整克隆当前
- 匹配车道的
Update处理revertLane回滚- 无
revertLane(无Suspense回滚)- 若已存在留存
baseQueue,克隆一个无优先级占位节点(lane=NoLane)存入链表 - 占位节点仅用于维持链表顺序,不再占用调度优先级
- 检测是否读取
Suspense纠缠资源,打标记
- 若已存在留存
- 存在
revertLane(Suspense异步回退)- 当前渲染包含回滚车道:直接丢弃该
Update,不存入baseQueue(回滚场景取消本次更新) - 当前渲染不含回滚车道:克隆存入
baseQueue,登记revertLane到Fiber待后续回滚渲染
- 当前渲染包含回滚车道:直接丢弃该
- 无
- 执行
reducer/eager预计算,迭代生成最新state - 环形循环收尾,组装新
baseQueue- 无留存更新:
newBaseState设为本次最终state,下一轮以此为计算起点 - 有留存更新:尾节点指向头节点,形成新环形
baseQueue
- 无留存更新:
- 对比
state是否变更,标记组件更新,读取Suspense异步资源则抛出Promise,触发Suspense渲染fallback,等待资源resolve后重试渲染 - 持久化本轮计算结果到
Hook,生成新baseState/baseQueue用于并发渲染中断恢复 - 无更新清空队列优先级
- 返回最新
state与稳定dispatch函数
设计意义
- 环形链表统一管理更新,支持多轮合并 :
pending/baseQueue全部采用环形链表,合并、遍历、留存逻辑统一,无链表首尾边界特殊判断,简化多批次更新合并逻辑 - 并发多优先级车道分层调度 :基于
Lanes子集判断,实现高优渲染优先执行、低优更新延后执行;跳过的更新不丢弃,低优先级update被克隆保留到新的baseQueue中,baseState记录跳过前的状态快照。下次渲染时,浏览器事件触发更高优先级,baseQueue中的低优先级update与新update合并后重新处理,保证状态最终一致性 eagerState同步预计算性能优化 :同步dispatch时提前算出state存入Update,渲染阶段直接复用,避免重复执行reducer,高频输入场景大幅降低计算开销dispatch引用稳定优化 :dispatch挂载在queue对象,挂载阶段仅bind创建一次,多轮渲染不生成新函数,配合memo/useCallback避免不必要重渲染
重渲染:rerenderState
渲染内递归更新专用,不做任何优先级过滤 ,一次性执行完所有 pending 渲染阶段更新
javascript
function rerenderState(initialState) {
return rerenderReducer(basicStateReducer, initialState);
}
function rerenderReducer(reducer, initialArg, init) {
// 1. 匹配克隆WIP Hook,同步推进新旧Hook游标
const hook = updateWorkInProgressHook(); // 复用 wip hook
const queue = hook.queue;
// 2. 记录本轮重渲染使用的reducer
queue.lastRenderedReducer = reducer;
// 3. 取出稳定dispatch函数(引用永远不变)
const dispatch = queue.dispatch;
// 4. 取出本轮渲染过程中新增的所有渲染阶段Update环形链表
const lastRenderPhaseUpdate = queue.pending; // 渲染阶段产生的 update
// 5. 初始基准state = 上一轮渲染完成的memoizedState
let newState = hook.memoizedState;
// 6. 判断本轮是否存在渲染内产生的更新
if (lastRenderPhaseUpdate !== null) {
// 清空pending,防止下一轮循环重复处理
queue.pending = null;
// 环形链表头节点
const firstRenderPhaseUpdate = lastRenderPhaseUpdate.next;
let update = firstRenderPhaseUpdate;
// 7. 环形遍历所有渲染阶段Update,无过滤全部执行
do {
const action = update.action;
newState = reducer(newState, action); // 直接处理(不检查优先级)
update = update.next;
} while (update !== first);
// 8. 状态发生变化,标记组件不能bailout
if (!is(newState, hook.memoizedState)) {
markWorkInProgressReceivedUpdate();
}
// 覆盖当前渲染使用的state
hook.memoizedState = newState;
// 9. 无持久化并发更新队列时,同步更新baseState
if (hook.baseQueue === null) {
hook.baseState = newState;
}
// 缓存本轮最终state到队列
queue.lastRenderedState = newState;
}
// 10. 返回 [最新state, 稳定dispatch]
return [hook.memoizedState, dispatch];
}
作用
- 同步推进
currentHook / workInProgressHook双游标,匹配克隆WIP Hook,维护统一Hooks单向链表,保证新旧Hook一一对应 - 取出本轮渲染过程中产生的全部临时
Update(render 内部 dispatch)环形链表,由enqueueRenderPhaseUpdate推入 - 存在渲染阶段更新,立刻清空
pending链表,避免下一轮renderWithHooksAgain重复处理同一批Update,防止无限循环叠加重复更新 - 环形链表遍历:不做任何优先级过滤,一次性顺序执行所有渲染内
dispatch更新,批量合并state - 状态变更标记:根据
state是否变更标记组件不可bailout,新旧state浅不相等时,将WIP Fiber didReceiveUpdate置为true;上层updateFunctionComponent不会触发bailout,必须完整调和子节点 - 同步更新渲染状态,仅当不存在遗留并发持久更新队列
baseQueue时,同步更新并发基准state - 缓存本轮最终计算
state,供下一轮渲染eager预计算读取基准状态 - 返回最新
state与引用稳定的dispatch,支撑循环重渲染判断
设计意义
- 渲染阶段更新无并发多优先级 :渲染阶段
setState(如setState在useEffect外被调用)会立即触发当前fiber重新渲染。这些update不需要优先级检查,因为它们是在当前渲染上下文中产生的,lane与当前renderLanes相同,不存在并发抢占、多优先级隔离问题,必须全部一次性合并计算,不存在 "延后到下一轮" 的场景 - 临时渲染更新一次性消耗 :渲染阶段
Update仅本轮循环有效,处理完直接清空pending,不存入持久并发基准队列baseQueue,并发中断恢复只针对跨渲染周期的异步 / 过渡更新
状态更新派发器:dispatchSetState
完成优先级分配、更新节点入环形队列,自动区分渲染阶段递归更新与普通并发更新,在安全前提下提供带异常容错的 eager 预计算性能优化,统一使用环形链表合并多次状态更新,完成更新入队、根追溯、并发调度与 Transition Suspense 绑定
ini
function dispatchSetState(
fiber,
queue,
action,
) {
// 1. 分配本次更新的优先级车道
const lane = requestUpdateLane(fiber);
// 2. 创建 Update 更新节点、执行入队/eager/调度核心逻辑
const didScheduleUpdate = dispatchSetStateInternal(
fiber,
queue,
action,
lane,
);
}
function dispatchSetStateInternal(
fiber,
queue,
action,
lane,
) {
// 1. 初始化本次更新 Update 节点
const update = {
lane,
revertLane: NoLane,
gesture: null,
action,
hasEagerState: false,
eagerState: null,
next: null,
};
// 分支A:判断是否为渲染阶段更新(render 内部直接调用 setState)
if (isRenderPhaseUpdate(fiber)) {
enqueueRenderPhaseUpdate(queue, update);
} else {
// 分支B:普通更新(事件/定时器/effect 等非渲染上下文)
const alternate = fiber.alternate;
// B1:满足 eager 预计算安全条件:当前fiber和alternate均无排队更新
if (
fiber.lanes === NoLanes &&
(alternate === null || alternate.lanes === NoLanes)
) {
const lastRenderedReducer = queue.lastRenderedReducer;
if (lastRenderedReducer !== null) {
let prevDispatcher = null;
try {
// 执行 eager 预计算,填充 eagerState
const currentState = queue.lastRenderedState;
const eagerState = lastRenderedReducer(currentState, action);
update.hasEagerState = true;
update.eagerState = eagerState;
// 预计算后新旧状态完全相等,短路丢弃更新
if (is(eagerState, currentState)) {
enqueueConcurrentHookUpdateAndEagerlyBailout(fiber, queue, update);
return false;
}
} catch (error) {
} finally {
}
}
}
// B2:将更新并入并发Hook更新队列,向上查找FiberRoot
const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
if (root !== null) {
// B3:找到根节点,发起全局渲染调度
scheduleUpdateOnFiber(root, fiber, lane);
// B4:标记transition纠缠更新,用于Suspense回退/中断恢复
entangleTransitionUpdate(root, queue, lane);
return true;
}
}
return false;
}
function dispatchReducerAction(
fiber,
queue
action,
) {
const lane = requestUpdateLane(fiber);
const update = {
lane,
revertLane: NoLane,
gesture: null,
action,
hasEagerState: false,
eagerState: null,
next: null,
};
if (isRenderPhaseUpdate(fiber)) {
enqueueRenderPhaseUpdate(queue, update);
} else {
const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
if (root !== null) {
scheduleUpdateOnFiber(root, fiber, lane);
entangleTransitionUpdate(root, queue, lane);
}
}
}
作用
-
根据当前代码执行上下文自动生成对应优先级
Lane -
创建
Update更新节点、执行入队/eager/调度核心逻辑,合并连续多次更新- 初始化本次更新
Update节点 - 渲染阶段更新:不立即调度渲染,存入渲染阶段专属临时队列 ,并全局标记
didScheduleRenderPhaseUpdateDuringThisPass = true;等待当前组件渲染完成后,进入renderWithHooksAgain循环重渲染合并更新,配合 50 次循环上限拦截无限死循环 - 普通非渲染上下文更新(事件、定时器、异步回调)
- 满足安全条件时同步预计算
eagerState,等值更新将更新标记为可直接丢弃,直接短路取消渲染,不发起调度 - 将当前
Update并入queue.pending环形并发更新链表 - 自底向上遍历
fiber父链,找到所属FiberRoot根节点并返回 - 给沿途所有祖先
fiber打上对应lanes,标记子树存在更新 - 根存在时,发起全局渲染调度,绑定
Transition纠缠更新用于Suspense设计意义
- 满足安全条件时同步预计算
- 初始化本次更新
-
单独隔离
render内递归更新逻辑,不混入普通并发调度;统一收敛到renderWithHooksAgain重试机制,双层防护无限渲染
设计思想
useState是useReducer特化 :basicStateReducer作为固定reducer,updateState直接委托updateReducer,减少代码重复,保留扩展性- 策略模式 :
HooksDispatcherOnMount/OnUpdate/OnRerender分离实现,渲染开始时一次切换到正确实现 - 惰性初始化 :
useState(() => initialState)的初始化函数只在mount时调用一次,避免重复计算 - 预计算优化(
Eager State) :空队列时立即计算新状态并Object.is比较,如果无变化,零开销跳过渲染 - 优先级驱动的
Update处理 :lane机制区分高/低优先级update,低优先级被跳过后保留在baseQueue中等待下次渲染回退 baseState + baseQueue回退 :跳过低优先级update时快照baseState,保证多次渲染后状态一致(不丢失低优先级更新)- 循环链表
Update队列 :queue.pending指向最后一个update,pending.next指向第一个,O(1) 合并两个队列 - 共享
UpdateQueue:current和workInProgress的hook共享同一个queue对象,queue.pending是diff标记 - 渲染阶段即时回放 :渲染期间
setState→rerender调度器 → 立即处理全部update→ 重新执行组件(限制 50 次防死循环) - 双链表保证
hooks顺序 :mountWorkInProgressHook追加到链表尾,updateWorkInProgressHook按next指针顺序遍历------调用顺序必须一致