⚛️ React Hooks 链表结构

深入理解 Hooks 底层实现原理

基于 React 19 源码深度解析
2026-03-13 | 技术深度解读

📑 目录概览

第一部分:基础架构
React Hooks 简介
核心数据结构
Hook 链表设计
第二部分:源码解析
useState 实现
useEffect 实现
其他 Hooks 实现
第三部分:核心机制
Dispatcher 模式
更新队列
渲染流程
第四部分:实践优化
性能优化策略
调试技巧
最佳实践

React Hooks 简介

Hooks 是 React 16.8 引入的新特性,让你在不编写 class 的情况下使用 state 以及其他的 React 特性。

核心优势:
  • 在组件之间复用状态逻辑
  • 将复杂组件拆分成更小的函数
  • 使用更少的代码,实现更清晰的结构
  • 无需 class,拥抱函数式编程
设计原则:
  • Hooks 在组件渲染时按固定顺序调用
  • 通过链表数据结构存储 Hooks 状态
  • 每个 Hook 独立管理自己的状态和副作用

Hooks 架构演进

版本 特性 数据结构
React 15 Class Component 实例属性
React 16.0 Fiber 架构 Fiber 树
React 16.8 Hooks API Hook 链表
React 18 并发模式 优先级队列
React 19 use(), Actions Thenable 支持

关键洞察:Hooks 的核心是使用链表在 Fiber 节点上存储状态,实现函数组件的状态持久化。

核心数据结构

Hook 对象
存储 Hook 的状态信息
通过 next 指针形成链表
Update 队列
存储状态更新
环形链表结构
Effect 链表
存储副作用回调
循环链表结构
Fiber 节点
React 工作单元
memoizedState 指向 Hook 链表

设计精髓:使用链表而非数组,是因为 Hooks 在渲染时按顺序调用,链表可以高效地动态扩展。

Hook 链表结构

export type Hook = {
  memoizedState: any,      // Hook 的记忆状态
  baseState: any,          // 基础状态(用于更新计算)
  baseQueue: Update | null,// 待处理的更新队列
  queue: any,              // 更新队列对象
  next: Hook | null,       // 指向下一个 Hook
};
链表关系图:
Fiber.memoizedState → Hook1 → Hook2 → Hook3 → null
  • memoizedState:存储当前 Hook 的状态值
  • next:指向下一个 Hook,形成单向链表
  • queue:useState/useReducer 的更新队列

Hook 对象详解

const hook: Hook = {
  memoizedState: null,     // 不同 Hook 存储不同内容
  baseState: null,         // 基础状态
  baseQueue: null,         // 基础更新队列
  queue: null,             // 更新调度器
  next: null,              // 下一个 Hook
};
Hook 类型 memoizedState 存储
useState state 值
useEffect Effect 对象
useRef {current: value}
useMemo [value, deps]
useCallback [callback, deps]

Fiber 与 Hooks 关系

function FiberNode(tag, pendingProps, key, mode) {
  // ... 其他属性
  
  // Hooks 链表的头节点
  this.memoizedState = null;
  
  // 更新队列(存储 Effects)
  this.updateQueue = null;
}
关键点:
  • Fiber 的 memoizedState 指向第一个 Hook
  • 每个 Hook 通过 next 连接下一个
  • 函数组件的 state 分散在各个 Hook 中

mountWorkInProgressHook - 初次挂载

function mountWorkInProgressHook(): Hook {
  const hook: Hook = {
    memoizedState: null,
    baseState: null,
    baseQueue: null,
    queue: null,
    next: null,
  };

  if (workInProgressHook === null) {
    // 第一个 Hook
    currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
  } else {
    // 追加到链表末尾
    workInProgressHook = workInProgressHook.next = hook;
  }
  return workInProgressHook;
}

核心逻辑:创建 Hook 节点,追加到链表末尾,返回当前 Hook。

updateWorkInProgressHook - 更新阶段

function updateWorkInProgressHook(): Hook {
  // 从 current fiber 获取下一个 Hook
  let nextCurrentHook;
  if (currentHook === null) {
    const current = currentlyRenderingFiber.alternate;
    nextCurrentHook = current !== null ? current.memoizedState : null;
  } else {
    nextCurrentHook = currentHook.next;
  }

  // 复用或创建新的 Hook
  const newHook: Hook = {
    memoizedState: currentHook.memoizedState,
    baseState: currentHook.baseState,
    baseQueue: currentHook.baseQueue,
    queue: currentHook.queue,
    next: null,
  };
  
  // 追加到 workInProgress 链表
  workInProgressHook = workInProgressHook.next = newHook;
  return workInProgressHook;
}

useState 链表实现

export function useState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
  const dispatcher = resolveDispatcher();
  return dispatcher.useState(initialState);
}

// 内部实现
function mountState<S>(initialState) {
  const hook = mountWorkInProgressHook();
  hook.memoizedState = hook.baseState = initialState;
  
  const queue = {
    pending: null,
    lanes: NoLanes,
    dispatch: null,
    lastRenderedReducer: basicStateReducer,
    lastRenderedState: initialState,
  };
  hook.queue = queue;
  
  const dispatch = dispatchSetState.bind(null, currentlyRenderingFiber, queue);
  queue.dispatch = dispatch;
  
  return [hook.memoizedState, dispatch];
}

useEffect 链表实现

function mountEffect(
  create: () => (() => void) | void,
  deps: Array<mixed> | void | null,
): void {
  const hook = mountWorkInProgressHook();
  const nextDeps = deps === undefined ? null : deps;
  
  currentlyRenderingFiber.flags |= PassiveEffect;
  
  hook.memoizedState = pushEffect(
    HookHasEffect | HookPassive,
    createEffectInstance(),
    create,
    nextDeps,
  );
}

关键:useEffect 的 memoizedState 存储的是 Effect 对象,而不是简单的状态值。

Update 队列结构

export type Update<S, A> = {
  lane: Lane,              // 优先级
  revertLane: Lane,        // 回退优先级(乐观更新)
  action: A,               // 更新动作
  hasEagerState: boolean,  // 是否已急切计算
  eagerState: S | null,    // 急切计算的状态
  next: Update<S, A>,      // 下一个更新(环形链表)
  gesture: null | ScheduledGesture,
};

export type UpdateQueue<S, A> = {
  pending: Update<S, A> | null,  // 待处理的更新(环形)
  lanes: Lanes,
  dispatch: ((A) => mixed) | null,
  lastRenderedReducer: ((S, A) => S) | null,
  lastRenderedState: S | null,
};

Effect 循环链表

export type Effect = {
  tag: HookFlags,          // Effect 类型标记
  inst: EffectInstance,    // 实例对象(存储 destroy)
  create: () => (() => void) | void,  // 创建函数
  deps: Array<mixed> | void | null,   // 依赖数组
  next: Effect,            // 指向下一个 Effect(循环)
};

function pushEffect(tag, inst, create, deps): Effect {
  const effect: Effect = { tag, inst, create, deps, next: null };
  
  const componentUpdateQueue = currentlyRenderingFiber.updateQueue;
  const lastEffect = componentUpdateQueue.lastEffect;
  
  if (lastEffect === null) {
    // 第一个 Effect,自己指向自己
    componentUpdateQueue.lastEffect = effect.next = effect;
  } else {
    // 插入到循环链表中
    const firstEffect = lastEffect.next;
    lastEffect.next = effect;
    effect.next = firstEffect;
    componentUpdateQueue.lastEffect = effect;
  }
  
  return effect;
}

Dispatcher 模式

React 使用 Dispatcher 对象来区分不同阶段的 Hooks 行为:

const HooksDispatcherOnMount: Dispatcher = {
  useState: mountState,
  useEffect: mountEffect,
  // ... mount 版本
};

const HooksDispatcherOnUpdate: Dispatcher = {
  useState: updateState,
  useEffect: updateEffect,
  // ... update 版本
};

const ContextOnlyDispatcher: Dispatcher = {
  useState: throwInvalidHookError,
  useEffect: throwInvalidHookError,
  // ... 在非组件上下文中抛错
};

作用:确保 Hooks 只能在函数组件内部调用,并在不同阶段执行不同逻辑。

渲染阶段 Hooks 初始化

export function renderWithHooks(
  current: Fiber | null,
  workInProgress: Fiber,
  Component: (p: Props) => any,
  props: Props,
  nextRenderLanes: Lanes,
): any {
  renderLanes = nextRenderLanes;
  currentlyRenderingFiber = workInProgress;
  
  // 重置 Hooks 链表
  workInProgress.memoizedState = null;
  workInProgress.updateQueue = null;
  
  // 设置对应的 Dispatcher
  ReactSharedInternals.H =
    current === null || current.memoizedState === null
      ? HooksDispatcherOnMount
      : HooksDispatcherOnUpdate;
  
  // 执行组件函数
  let children = Component(props);
  
  // 重置
  ReactSharedInternals.H = ContextOnlyDispatcher;
  
  return children;
}

更新阶段 Hooks 处理

Mount 阶段
  • 创建新的 Hook 链表
  • 初始化状态值
  • 创建更新队列
Update 阶段
  • 复用已有 Hook 链表
  • 处理更新队列
  • 计算新状态

重要:Hooks 的顺序必须保持一致,否则会导致状态错乱!

Hooks 调用顺序的重要性

function Component() {
  const [count, setCount] = useState(0);  // Hook 1
  const [name, setName] = useState('');   // Hook 2
  useEffect(() => {});                    // Hook 3
}

// 第一次渲染:
// Fiber.memoizedState → Hook1(count) → Hook2(name) → Hook3(effect)

// 第二次渲染:
// 必须按相同顺序调用,才能正确复用状态!

// ❌ 错误示例:条件调用
function BadComponent({ isLoggedIn }) {
  if (isLoggedIn) {
    const [user, setUser] = useState(null); // 顺序变化!
  }
}

规则:只能在函数最顶层调用 Hooks,不要在循环、条件或嵌套函数中调用。

renderWithHooks 完整流程

  1. 设置渲染上下文(renderLanes, currentlyRenderingFiber)
  2. 重置 workInProgress 的 memoizedState 和 updateQueue
  3. 根据 current 选择 Dispatcher(Mount/Update)
  4. 执行组件函数,调用各种 Hooks
    • 每个 Hook 创建/复用 Hook 节点
    • 追加到 workInProgressHook 链表
  5. 检查是否少于上次渲染的 Hooks 数量(错误检测)
  6. 重置全局状态,返回 children

useState 源码解析

// ReactHooks.js - 入口
export function useState<S>(initialState) {
  const dispatcher = resolveDispatcher();
  return dispatcher.useState(initialState);
}

// ReactFiberHooks.js - 实现
function mountState<S>(initialState) {
  const hook = mountWorkInProgressHook();
  
  // 初始化状态
  if (typeof initialState === 'function') {
    initialState = initialState();
  }
  hook.memoizedState = hook.baseState = initialState;
  
  // 创建更新队列
  const queue: UpdateQueue = {
    pending: null,
    lanes: NoLanes,
    dispatch: null,
    lastRenderedReducer: basicStateReducer,
    lastRenderedState: initialState,
  };
  hook.queue = queue;
  
  // 绑定 dispatch
  const dispatch = dispatchSetState.bind(
    null, 
    currentlyRenderingFiber, 
    queue
  );
  queue.dispatch = dispatch;
  
  return [hook.memoizedState, dispatch];
}

mountState 实现细节

步骤 1:创建 Hook 节点
const hook = mountWorkInProgressHook();
步骤 2:初始化状态
hook.memoizedState = hook.baseState = initialState;
步骤 3:创建更新队列
const queue = { pending: null, dispatch: null, ... };
步骤 4:绑定 dispatch
const dispatch = dispatchSetState.bind(null, fiber, queue);

updateState 实现

function updateState<S>(initialState) {
  // updateState 本质是 updateReducer 的特殊形式
  return updateReducer(basicStateReducer, initialState);
}

function basicStateReducer<S>(state: S, action: BasicStateAction<S>): S {
  return typeof action === 'function' ? action(state) : action;
}

function updateReducer<S, A>(reducer, initialArg, init?) {
  const hook = updateWorkInProgressHook();
  const queue = hook.queue;
  
  // 处理更新队列...
  let newState = hook.baseState;
  let update = queue.pending;
  
  do {
    newState = reducer(newState, update.action);
    update = update.next;
  } while (update !== queue.pending);
  
  hook.memoizedState = newState;
  return [hook.memoizedState, queue.dispatch];
}

dispatchSetState 实现

function dispatchSetState<S, A>(
  fiber: Fiber,
  queue: UpdateQueue<S, A>,
  action: A,
): void {
  const lane = requestUpdateLane(fiber);
  
  const update: Update<S, A> = {
    lane,
    revertLane: NoLane,
    action,
    hasEagerState: false,
    eagerState: null,
    next: null,
  };
  
  if (isRenderPhaseUpdate(fiber)) {
    // 渲染阶段更新:加入 pending 队列
    enqueueRenderPhaseUpdate(queue, update);
  } else {
    // 调度更新
    const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
    if (root !== null) {
      scheduleUpdateOnFiber(root, fiber, lane);
    }
  }
}

useEffect 源码解析

// ReactHooks.js - 入口
export function useEffect(create, deps) {
  const dispatcher = resolveDispatcher();
  return dispatcher.useEffect(create, deps);
}

// ReactFiberHooks.js - mount 版本
function mountEffect(create, deps) {
  return mountEffectImpl(
    PassiveEffect | PassiveStaticEffect,
    HookPassive,
    create,
    deps,
  );
}

function mountEffectImpl(fiberFlags, hookFlags, create, deps) {
  const hook = mountWorkInProgressHook();
  const nextDeps = deps === undefined ? null : deps;
  
  currentlyRenderingFiber.flags |= fiberFlags;
  
  hook.memoizedState = pushEffect(
    HookHasEffect | hookFlags,
    createEffectInstance(),
    create,
    nextDeps,
  );
}

mountEffect 实现细节

1. 创建 Hook
const hook = mountWorkInProgressHook();
2. 标记 Fiber
fiber.flags |= PassiveEffect;
3. 创建 Effect
const effect = {
  tag: HookHasEffect | HookPassive,
  create: create,
  deps: deps,
  // ...
};
4. 加入循环链表
pushEffect(effect);

注意:Effect 存储在 Fiber.updateQueue 的循环链表中,而不是 Hook.memoizedState。

updateEffect 实现

function updateEffect(create, deps) {
  return updateEffectImpl(PassiveEffect, HookPassive, create, deps);
}

function updateEffectImpl(fiberFlags, hookFlags, create, deps) {
  const hook = updateWorkInProgressHook();
  const nextDeps = deps === undefined ? null : deps;
  
  const effect: Effect = hook.memoizedState;
  const inst = effect.inst;
  
  // 比较依赖
  if (currentHook !== null) {
    if (nextDeps !== null) {
      const prevEffect = currentHook.memoizedState;
      const prevDeps = prevEffect.deps;
      
      if (areHookInputsEqual(nextDeps, prevDeps)) {
        // 依赖未变,只推入 effect 但不标记 HasEffect
        hook.memoizedState = pushEffect(hookFlags, inst, create, nextDeps);
        return;
      }
    }
  }
  
  // 依赖改变,标记 HasEffect
  currentlyRenderingFiber.flags |= fiberFlags;
  hook.memoizedState = pushEffect(
    HookHasEffect | hookFlags,
    inst,
    create,
    nextDeps,
  );
}

pushEffect 实现 - 循环链表插入

function pushEffect(tag, inst, create, deps): Effect {
  const effect: Effect = {
    tag,
    inst,
    create,
    deps,
    next: null,
  };
  
  let componentUpdateQueue = currentlyRenderingFiber.updateQueue;
  if (componentUpdateQueue === null) {
    componentUpdateQueue = createFunctionComponentUpdateQueue();
    currentlyRenderingFiber.updateQueue = componentUpdateQueue;
  }
  
  const lastEffect = componentUpdateQueue.lastEffect;
  if (lastEffect === null) {
    // 第一个 effect,自引用形成循环
    componentUpdateQueue.lastEffect = effect.next = effect;
  } else {
    // 插入到最后一个和第一个之间
    const firstEffect = lastEffect.next;
    lastEffect.next = effect;
    effect.next = firstEffect;
    componentUpdateQueue.lastEffect = effect;
  }
  
  return effect;
}

useReducer 链表实现

function mountReducer<S, I, A>(reducer, initialArg, init?) {
  const hook = mountWorkInProgressHook();
  
  let initialState;
  if (init !== undefined) {
    initialState = init(initialArg);
  } else {
    initialState = initialArg;
  }
  
  hook.memoizedState = hook.baseState = initialState;
  
  const queue: UpdateQueue<S, A> = {
    pending: null,
    lanes: NoLanes,
    dispatch: null,
    lastRenderedReducer: reducer,
    lastRenderedState: initialState,
  };
  hook.queue = queue;
  
  const dispatch = dispatchReducerAction.bind(
    null,
    currentlyRenderingFiber,
    queue,
  );
  queue.dispatch = dispatch;
  
  return [hook.memoizedState, dispatch];
}

与 useState 的区别:useState 使用 basicStateReducer,useReducer 使用用户提供的 reducer。

useMemo/useCallback 实现

function mountMemo<T>(nextCreate: () => T, deps) {
  const hook = mountWorkInProgressHook();
  const nextDeps = deps === undefined ? null : deps;
  const nextValue = nextCreate();
  
  hook.memoizedState = [nextValue, nextDeps];
  return nextValue;
}

function updateMemo<T>(nextCreate: () => T, 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;
}

// useCallback 本质是 useMemo 的特例
function mountCallback(callback, deps) {
  const hook = mountWorkInProgressHook();
  hook.memoizedState = [callback, deps];
  return callback;
}

useRef 实现

function mountRef<T>(initialValue: T): {current: T} {
  const hook = mountWorkInProgressHook();
  const ref = {current: initialValue};
  hook.memoizedState = ref;
  return ref;
}

function updateRef<T>(initialValue: T): {current: T} {
  const hook = updateWorkInProgressHook();
  return hook.memoizedState; // 直接返回已有的 ref 对象
}
关键特性:
  • ref 对象在组件生命周期内保持不变
  • 修改 ref.current 不会触发重新渲染
  • 适合存储 DOM 引用或任意可变值

useContext 实现

export function useContext<T>(Context: ReactContext<T>): T {
  const dispatcher = resolveDispatcher();
  return dispatcher.useContext(Context);
}

// 在 Dispatcher 中
useContext: readContext,

// ReactFiberNewContext.js
export function readContext<T>(context: ReactContext<T>): T {
  // 读取当前 Fiber 的 context 值
  // 不需要 Hook 节点!
  return context._currentValue;
}

注意:useContext 不创建 Hook 节点,直接从 Context 对象读取值。但会订阅 Context 变化。

useTransition 实现

function mountTransition() {
  const stateHook = mountStateImpl(false);
  
  // startTransition 函数是稳定的
  const start = startTransition.bind(
    null,
    currentlyRenderingFiber,
    stateHook.queue,
    true,   // pendingState
    false,  // finishedState
  );
  
  const hook = mountWorkInProgressHook();
  hook.memoizedState = start;
  
  return [false, start];
}

function updateTransition() {
  const [booleanOrThenable] = updateState(false);
  const hook = updateWorkInProgressHook();
  const start = hook.memoizedState;
  
  const isPending = typeof booleanOrThenable === 'boolean'
    ? booleanOrThenable
    : useThenable(booleanOrThenable);
  
  return [isPending, start];
}

useDeferredValue 实现

function updateDeferredValue<T>(value: T, initialValue?: T): T {
  const hook = updateWorkInProgressHook();
  const prevValue: T = currentHook.memoizedState;
  
  if (is(value, prevValue)) {
    // 值未变,直接返回
    return value;
  }
  
  const shouldDeferValue = 
    !includesOnlyNonUrgentLanes(renderLanes) && 
    !isRenderingDeferredWork();
  
  if (shouldDeferValue) {
    // 紧急更新:延迟渲染新值
    const deferredLane = requestDeferredLane();
    currentlyRenderingFiber.lanes = mergeLanes(
      currentlyRenderingFiber.lanes,
      deferredLane,
    );
    markSkippedUpdateLanes(deferredLane);
    
    return prevValue; // 返回旧值
  } else {
    // 非紧急:直接使用新值
    markWorkInProgressReceivedUpdate();
    hook.memoizedState = value;
    return value;
  }
}

useId 实现

function mountId(): string {
  const hook = mountWorkInProgressHook();
  const root = getWorkInProgressRoot();
  const identifierPrefix = root.identifierPrefix;
  
  let id;
  if (getIsHydrating()) {
    // SSR hydration:使用服务端生成的 ID
    const treeId = getTreeId();
    id = '_' + identifierPrefix + 'R_' + treeId;
    
    const localId = localIdCounter++;
    if (localId > 0) {
      id += 'H' + localId.toString(32);
    }
    id += '_';
  } else {
    // 客户端:生成唯一 ID
    const globalClientId = globalClientIdCounter++;
    id = '_' + identifierPrefix + 'r_' + 
         globalClientId.toString(32) + '_';
  }
  
  hook.memoizedState = id;
  return id;
}

Hooks 规则原理

规则 1:只在顶层调用

不要在循环、条件或嵌套函数中调用 Hooks

原因:Hooks 依赖调用顺序来匹配状态

规则 2:只在 React 函数中调用

只在函数组件或自定义 Hook 中调用

原因:需要 Dispatcher 上下文

// ❌ 违反规则
if (condition) {
  const [value, setValue] = useState(0); // 顺序不稳定
}

// ✅ 正确
const [value, setValue] = useState(0);
if (condition) {
  // 使用 value
}

条件调用检测(DEV 模式)

// React 在 DEV 模式下检测 Hooks 调用顺序
let hookTypesDev: Array<HookType> | null = null;
let hookTypesUpdateIndexDev: number = -1;

function updateHookTypesDev() {
  const hookName = currentHookNameInDev;
  
  if (hookTypesDev !== null) {
    hookTypesUpdateIndexDev++;
    if (hookTypesDev[hookTypesUpdateIndexDev] !== hookName) {
      warnOnHookMismatchInDev(hookName); // 警告顺序不匹配
    }
  }
}

function warnOnHookMismatchInDev(currentHookName) {
  console.error(
    'React has detected a change in the order of Hooks called by %s.',
    componentName
  );
}

闭包陷阱分析

function Counter() {
  const [count, setCount] = useState(0);
  
  useEffect(() => {
    const id = setInterval(() => {
      console.log(count); // ❌ 永远是 0(闭包捕获)
    }, 1000);
    return () => clearInterval(id);
  }, []); // 空依赖数组
}

// 解决方案 1:添加依赖
useEffect(() => {
  const id = setInterval(() => {
    console.log(count); // ✅ 每次都是最新值
  }, 1000);
  return () => clearInterval(id);
}, [count]); // 添加依赖

// 解决方案 2:使用 useRef
const countRef = useRef(count);
countRef.current = count;
useEffect(() => {
  const id = setInterval(() => {
    console.log(countRef.current); // ✅ 始终最新
  }, 1000);
  return () => clearInterval(id);
}, []);

Stale Closure 问题

问题根源:
  • 函数组件每次渲染都创建新的函数作用域
  • useEffect/useCallback 的回调会捕获创建时的变量
  • 如果依赖不完整,回调中的变量就是过期的
// ❌ Stale Closure
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
  console.log(count); // 捕获初始 count
}, []); // 缺少 count 依赖

// ✅ 正确做法
const handleClick = useCallback(() => {
  console.log(count);
}, [count]); // 添加 count 依赖

// ✅ 或使用函数式更新
const handleClick = useCallback(() => {
  setCount(c => {
    console.log(c); // 使用最新的 c
    return c + 1;
  });
}, []); // 不需要 count 依赖

性能优化策略

策略 实现 效果
useMemo 缓存计算结果 避免重复计算
useCallback 缓存函数引用 避免子组件重渲染
React.memo 浅比较 props 跳过不必要的渲染
useDeferredValue 延迟更新 保持 UI 响应
useTransition 非阻塞更新 优先级调度

原则:不要过早优化,先测量性能瓶颈,再针对性优化。

依赖数组优化

// ❌ 过度依赖
useEffect(() => {
  doSomething(config.url, config.method);
}, [config.url, config.method]); // 分别列出

// ✅ 使用 useMemo 减少依赖
const configDeps = useMemo(() => ({
  url: config.url,
  method: config.method,
}), [config.url, config.method]);

useEffect(() => {
  doSomething(configDeps);
}, [configDeps]); // 单一依赖

// ❌ 每次渲染都重新创建
const fetchData = useCallback(() => {
  fetch(url + query);
}, [url, query]); // query 可能频繁变化

// ✅ 使用 ref 存储可变值
const queryRef = useRef(query);
queryRef.current = query;

const fetchData = useCallback(() => {
  fetch(url + queryRef.current);
}, [url]); // 减少依赖

Eager State 优化

function dispatchSetStateInternal(fiber, queue, action, lane) {
  // 如果当前没有其他更新,可以急切计算新状态
  if (
    fiber.lanes === NoLanes &&
    (alternate === null || alternate.lanes === NoLanes)
  ) {
    const lastRenderedReducer = queue.lastRenderedReducer;
    if (lastRenderedReducer !== null) {
      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; // 不需要调度更新
      }
    }
  }
  
  // 需要调度更新
  const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
  scheduleUpdateOnFiber(root, fiber, lane);
  return true;
}

Bailout 机制 - 跳过渲染

React 组件跳过渲染的条件:
  1. props 引用相等(React.memo 浅比较)
  2. state 引用相等(Object.is 比较)
  3. context 值未变化
  4. Fiber.lanes === NoLanes(无待处理更新)
// bailoutHooks 实现
export function bailoutHooks(
  current: Fiber,
  workInProgress: Fiber,
  lanes: Lanes,
): void {
  workInProgress.updateQueue = current.updateQueue;
  workInProgress.flags &= ~(PassiveEffect | UpdateEffect);
  current.lanes = removeLanes(current.lanes, lanes);
}

// 使用场景
if (current !== null && current.memoizedState !== null) {
  // 有现有状态,检查是否可以 bailout
  if (checkDidRenderIdHook()) {
    // 状态未变,跳过渲染
    return bailoutHooks(current, workInProgress, renderLanes);
  }
}

并发模式 Hooks 特性

useTransition
  • 标记非紧急更新
  • isPending 状态追踪
  • 保持 UI 响应性
useDeferredValue
  • 延迟值更新
  • 后台渲染新值
  • 防抖效果
Suspense 集成
  • use() 读取 Promise
  • 自动触发 Suspense
  • 数据加载状态
优先级调度
  • Lane 模型
  • 中断/恢复渲染
  • 用户交互优先

Strict Mode 双重调用

// 在 Strict Mode 下,组件会渲染两次
const shouldDoubleRenderDEV =
  __DEV__ && (workInProgress.mode & StrictLegacyMode) !== NoMode;

shouldDoubleInvokeUserFnsInHooksDEV = shouldDoubleRenderDEV;

let children = Component(props);

if (shouldDoubleRenderDEV) {
  // 第二次渲染
  setIsStrictModeForDevtools(true);
  try {
    children = renderWithHooksAgain(
      workInProgress,
      Component,
      props,
      secondArg,
    );
  } finally {
    setIsStrictModeForDevtools(false);
  }
}

目的:帮助发现副作用和不纯的渲染逻辑,确保组件可以安全地多次渲染。

DevTools 集成

// React DevTools 可以读取 Hooks 链表
if (__DEV__) {
  workInProgress._debugHookTypes = hookTypesDev;
  
  // 存储 thenable state 用于调试
  if (workInProgress.dependencies === null) {
    if (thenableState !== null) {
      workInProgress.dependencies = {
        lanes: NoLanes,
        firstContext: null,
        _debugThenableState: thenableState,
      };
    }
  }
}

// DevTools 可以访问:
// - fiber._debugHookTypes: Hook 类型数组
// - fiber.memoizedState: Hook 链表
// - 每个 Hook 的 memoizedState

调试技巧

1. 使用 useDebugValue
function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);
  
  // 在 DevTools 中显示
  useDebugValue(isOnline ? 'Online' : 'Offline');
  
  return isOnline;
}
2. 打印 Hook 链表
// 在控制台访问当前组件的 Fiber
const fiber = document.querySelector('#root')._reactRootContainer._internalRoot.current;
let hook = fiber.memoizedState;
while (hook) {
  console.log('Hook:', hook);
  hook = hook.next;
}

最佳实践

✅ 推荐做法
  • 自定义 Hook 提取复用逻辑
  • 使用 ESLint exhaustive-deps 规则
  • 合理拆分组件
  • 使用函数式更新
  • 及时清理副作用
性能优化
  • useMemo 缓存计算
  • useCallback 缓存回调
  • React.memo 避免重渲染
  • useDeferredValue 延迟
  • 虚拟列表长列表

常见反模式

// ❌ 在循环中调用 Hooks
items.forEach(item => {
  const [value, setValue] = useState(item.value);
});

// ❌ 在条件语句中调用 Hooks
if (condition) {
  const [value, setValue] = useState(0);
}

// ❌ 在普通函数中调用 Hooks
function handleClick() {
  const [value, setValue] = useState(0); // 错误!
}

// ❌ 过度使用 useMemo
const value = useMemo(() => a + b, [a, b]); // 简单计算不需要

// ❌ 忘记清理副作用
useEffect(() => {
  const id = setInterval(() => {}, 1000);
  // 缺少 return () => clearInterval(id);
}, []);

扩展阅读

官方资源:
  • React 官方文档 - Hooks API Reference
  • React 源码 - packages/react-reconciler/src/ReactFiberHooks.js
  • React RFC - Hooks 动机与设计
深入理解:
  • "Making sense of React Hooks" - Dan Abramov
  • React Fiber Architecture - Andrew Clark
  • React Concurrent Mode 指南
相关主题:
  • React Reconciler 工作原理
  • React Scheduler 调度机制
  • React Suspense 与数据获取

总结

核心要点
  • Hooks 使用链表存储状态
  • Fiber.memoizedState 指向链表头
  • 调用顺序决定状态匹配
  • Dispatcher 区分不同阶段
关键数据结构
  • Hook: 单向链表
  • Update: 环形链表
  • Effect: 循环链表
  • Dispatcher: 对象模式

理解 Hooks 链表结构,是掌握 React 内部机制的关键!

⚛️ 谢谢观看

React Hooks 链表结构深度解析

2026-03-13 | 查克小助理 🦞