React Hooks 内部实现原理与函数组件更新机制

发布于:2025-07-07 ⋅ 阅读:(22) ⋅ 点赞:(0)

React Hooks 内部实现原理与函数组件更新机制

Hooks 的内部实现原理

React Hooks 的实现依赖于以下几个关键机制:

1. 链表结构存储 Hook 状态

React 使用单向链表来管理 Hooks 的状态。每个 Hook 节点包含:

type Hook = {
  memoizedState: any,      // 存储当前状态
  baseState: any,          // 基础状态
  baseQueue: Update<any, any> | null, // 基础更新队列
  queue: UpdateQueue<any, any> | null, // 更新队列
  next: Hook | null,       // 指向下一个 Hook
};

2. 当前 Hook 指针

React 内部维护一个 currentHook 指针,它会随着组件的渲染过程依次指向链表中的每个 Hook:

let currentlyRenderingFiber: Fiber | null = null;
let currentHook: Hook | null = null;
let workInProgressHook: Hook | null = null;

3. Hooks 调用顺序的重要性

Hooks 必须无条件地在组件顶层调用,这是因为 React 依赖于调用顺序来正确关联 Hook 和它的状态:

function updateFunctionComponent(fiber) {
  // 重置指针
  currentlyRenderingFiber = fiber;
  fiber.memoizedHooks = null;
  currentHook = null;
  workInProgressHook = null;
  
  // 执行组件函数
  const children = Component(props);
  
  // 渲染完成后重置
  currentlyRenderingFiber = null;
  currentHook = null;
  workInProgressHook = null;
  
  return children;
}

函数组件更新机制

1. 调度阶段

当状态更新时,React 会:

  1. 创建一个更新对象并加入更新队列
  2. 调度一次新的渲染(通过 scheduleUpdateOnFiber

2. 渲染阶段

在渲染阶段,React 会:

  1. 调用函数组件
  2. 按顺序执行 Hooks
  3. 返回新的 React 元素
function renderWithHooks(current, workInProgress, Component, props) {
  // 设置当前正在渲染的 Fiber
  currentlyRenderingFiber = workInProgress;
  
  // 重置 Hook 链表
  workInProgress.memoizedState = null;
  
  // 执行组件函数
  const children = Component(props);
  
  // 重置状态
  currentlyRenderingFiber = null;
  
  return children;
}

3. 提交阶段

在提交阶段,React 会将渲染结果应用到 DOM 上,并执行副作用(useEffect 等)。

常见 Hook 的实现原理

useState

function useState(initialState) {
  return useReducer(
    basicStateReducer,
    initialState
  );
}

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

useEffect

function useEffect(create, deps) {
  const fiber = currentlyRenderingFiber;
  const effect = {
    tag: HookEffectTag, // 标识是 effect
    create,             // 副作用函数
    destroy: undefined,  // 清理函数
    deps,               // 依赖数组
    next: null,         // 下一个 effect
  };
  
  // 将 effect 添加到 fiber 的 updateQueue
  if (fiber.updateQueue === null) {
    fiber.updateQueue = { lastEffect: null };
  }
  const lastEffect = fiber.updateQueue.lastEffect;
  
  if (lastEffect === null) {
    fiber.updateQueue.lastEffect = effect.next = effect;
  } else {
    const firstEffect = lastEffect.next;
    lastEffect.next = effect;
    effect.next = firstEffect;
    fiber.updateQueue.lastEffect = effect;
  }
}

useMemo

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

关键点总结

  1. Hooks 依赖于调用顺序:React 使用链表结构按顺序存储 Hook 状态,因此不能在条件或循环中使用 Hooks。

  2. 双缓存技术:React 使用 current 和 workInProgress 两棵树来实现异步可中断的渲染。

  3. 闭包陷阱:函数组件每次渲染都会创建新的闭包,这解释了为什么有时候会拿到旧的 state 或 props。

  4. 批量更新:React 会合并多个状态更新,避免不必要的重复渲染。

  5. 副作用调度:useEffect 的副作用会在浏览器完成布局与绘制之后延迟执行。

理解这些原理有助于编写更高效、更可靠的 React 应用,并能更好地调试 Hook 相关的问题。