useState 源码解析

数据结构

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 指向最后一个 updatepending.next第一个 update
  • dispatchSetState:底层通用派发更新逻辑,接收 fiber、队列、新状态值,入队更新并触发调度
  • baseState/baseQueue:用于低优先级更新回退 ,跳过的 update 保留在 baseQueuebaseState 是跳过的前一个状态

挂载: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 派发函数,每个 useStatedispatch 是独立函数实例
  • 存储 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 调用顺序可复现。所有 HooksuseState/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 的返回值

设计意义

useStateuseReducerupdate 阶段共享同一套更新计算逻辑。区别只在于 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 环形链表尾部
    • 合并 updateLanecurrentlyRenderingFiber.lanes,标记当前 Fiber 存在未处理更新
    • markSkippedUpdateLanes 全局登记被跳过的车道,调度器后续会重新调度该根渲染
  • 匹配车道的 Update 处理 revertLane 回滚
    • revertLane(无 Suspense 回滚)
      • 若已存在留存 baseQueue,克隆一个无优先级占位节点(lane=NoLane)存入链表
      • 占位节点仅用于维持链表顺序,不再占用调度优先级
      • 检测是否读取 Suspense 纠缠资源,打标记
    • 存在 revertLaneSuspense 异步回退)
      • 当前渲染包含回滚车道:直接丢弃该 Update,不存入 baseQueue(回滚场景取消本次更新)
      • 当前渲染不含回滚车道:克隆存入 baseQueue,登记 revertLaneFiber 待后续回滚渲染
  • 执行 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(如 setStateuseEffect 外被调用)会立即触发当前 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 重试机制,双层防护无限渲染


设计思想

  • useStateuseReducer 特化basicStateReducer 作为固定 reducerupdateState 直接委托 updateReducer,减少代码重复,保留扩展性
  • 策略模式HooksDispatcherOnMount/OnUpdate/OnRerender 分离实现,渲染开始时一次切换到正确实现
  • 惰性初始化useState(() => initialState) 的初始化函数只在 mount 时调用一次,避免重复计算
  • 预计算优化(Eager State :空队列时立即计算新状态并 Object.is 比较,如果无变化,零开销跳过渲染
  • 优先级驱动的 Update 处理lane 机制区分高/低优先级 update,低优先级被跳过后保留在 baseQueue 中等待下次渲染回退
  • baseState + baseQueue 回退 :跳过低优先级 update 时快照 baseState,保证多次渲染后状态一致(不丢失低优先级更新)
  • 循环链表 Update 队列queue.pending 指向最后一个 updatepending.next 指向第一个,O(1) 合并两个队列
  • 共享 UpdateQueuecurrentworkInProgresshook 共享同一个 queue 对象,queue.pendingdiff 标记
  • 渲染阶段即时回放 :渲染期间 setStatererender 调度器 → 立即处理全部 update → 重新执行组件(限制 50 次防死循环)
  • 双链表保证 hooks 顺序mountWorkInProgressHook 追加到链表尾,updateWorkInProgressHooknext 指针顺序遍历------调用顺序必须一致
相关推荐
Flynt8 小时前
我的Next.js项目升级到16之后,dev倒是快了,但build差点让我回退
react.js·next.js·turbopack
光影少年1 天前
HashRouter 和 BrowserRouter 区别、底层原理、部署差异
前端·react.js·nestjs
kyriewen2 天前
我用 50 行代码重写了 React Router 核心,终于搞懂了前端路由原理
前端·javascript·react.js
ZhengEnCi2 天前
Q02-Vue-React-index.html完全指南
vue.js·react.js·html
weedsfly2 天前
JavaScript 事件流:彻底搞懂捕获、冒泡与事件委托
前端·javascript·react.js
光影少年4 天前
原生DOM操作在React 中的注意事项
前端·javascript·react.js
YAwu115 天前
深入解析 React 炫彩鼠标跟随标题组件:从坐标定位到动画性能
前端·react.js
Ruihong5 天前
🎉 VuReact 1.9.0 发布,支持 Vue 3.4 defineModel 编译到 React
vue.js·react.js·面试
spmcor5 天前
React 架构师之路:Next.js 全栈革命(第八篇)
前端·react.js