Skip to content

7.2 useState: 状态的实现与更新

useState 是 React 中最基础、最常用的 Hook,它为函数组件提供了内部状态(state)管理的能力。它的实现巧妙地利用了我们在上一节中讨论的 Hooks 链表和 Dispatcher 机制,并根据组件的渲染阶段(mount 或 update)提供了不同的逻辑。

从根本上说,useStateuseReducer 的一个特例。React 内部通过 mountStateupdateState 两个函数来实现 useState,它们分别对应组件的首次渲染和更新渲染。

1. 首次渲染:mountState

当组件首次渲染时,ReactSharedInternals.H 指向 HooksDispatcherOnMount(或其 DEV 版本),此时调用 useState 实际上会执行 mountState 函数。

mountState 的核心职责是:

  1. 初始化 Hook,并将其添加到当前 Fiber 的 Hooks 链表中。
  2. 计算初始 state。
  3. 创建一个 dispatch 函数,用于后续的状态更新。
  4. 返回 [state, dispatch]

让我们深入源码,看看 mountState 的具体实现:

javascript
// In packages/react-reconciler/src/ReactFiberHooks.js

function mountState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
  // 1. 创建并初始化 Hook
  const hook = mountWorkInProgressHook();

  // 2. 计算初始 state
  if (typeof initialState === 'function') {
    // 如果 initialState 是一个函数,则执行它以获取初始值
    initialState = initialState();
  }
  hook.memoizedState = hook.baseState = initialState;

  // 3. 创建更新队列和 dispatch 函数
  const queue: UpdateQueue<S, BasicStateAction<S>> = {
    pending: null,
    lanes: NoLanes,
    dispatch: null,
    lastRenderedReducer: basicStateReducer, // 使用内置的 reducer
    lastRenderedState: (initialState: any),
  };
  hook.queue = queue;
  const dispatch: Dispatch<BasicStateAction<S>> = (queue.dispatch =
    (dispatchAction.bind(null, currentlyRenderingFiber, queue): any));

  // 4. 返回 state 和 dispatch
  return [hook.memoizedState, dispatch];
}

这里的 basicStateReducer 是一个非常简单的 reducer,它处理 useState 的两种更新方式:直接传入新值,或者传入一个接收前一个 state 并返回新 state 的函数。

javascript
// In packages/react-reconciler/src/ReactFiberHooks.js

function basicStateReducer<S>(state: S, action: BasicStateAction<S>): S {
  // $FlowFixMe: Flow doesn't like mixed types
  return typeof action === 'function' ? action(state) : action;
}

2. 更新渲染:updateState

当组件进行更新渲染时,Dispatcher 会切换到 HooksDispatcherOnUpdate,此时调用 useState 会执行 updateState 函数。updateState 内部会调用 updateReducer

updateState 的核心职责是:

  1. 从 Hooks 链表中获取当前的 Hook 对象。
  2. 基于前一次的 state 和 updateQueue 中的 Update 对象,计算出最新的 state。
  3. 返回 [newState, dispatch]
javascript
// In packages/react-reconciler/src/ReactFiberHooks.js

function updateState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
  return updateReducer(basicStateReducer, (initialState: any));
}

function updateReducer<S, I, A>(
  reducer: (S, A) => S,
  initialArg: I,
  init?: I => S,
): [S, Dispatch<A>] {
  // 1. 获取当前 Hook
  const hook = updateWorkInProgressHook();
  const queue = hook.queue;

  // ... 省略部分代码

  const dispatch: Dispatch<A> = (queue.dispatch: any);

  // ... 省略 updateReducer 内部复杂的 state 计算逻辑

  // 2. 计算新 state
  const newState = hook.memoizedState;

  // 3. 返回新 state 和 dispatch
  return [newState, dispatch];
}

updateReducer 的逻辑比 mountReducer 复杂得多,它需要处理 Update 队列,合并多个更新,并根据优先级跳过某些更新。我们将在后续章节中更详细地探讨更新队列的机制。

3. 状态更新的发起者:dispatchAction

无论是在 mount 还是 update 阶段,useState 返回的第二个参数都是一个 dispatch 函数。这个函数的核心是 dispatchAction。当你调用 setState(newState) 时,实际上就是在调用 dispatchAction

dispatchAction 的主要工作是:

  1. 创建一个 Update 对象,其中包含了新的 action(即新的 state)和当前的 lane(优先级)。
  2. 将这个 Update 对象加入到 Hook 的 updateQueue 中。
  3. 调度一个新的 React 更新。
javascript
// In packages/react-reconciler/src/ReactFiberHooks.js

function dispatchAction<S, A>(
  fiber: Fiber,
  queue: UpdateQueue<S, A>,
  action: A,
) {
  // 1. 获取当前更新的 lane
  const lane = requestUpdateLane(fiber);

  // 2. 创建 Update 对象
  const update: Update<S, A> = {
    lane,
    revertLane: NoLane,
    action,
    hasEagerState: false,
    eagerState: null,
    next: (null: any),
    gesture: null,
  };

  // 3. 将 Update 入队并调度更新
  const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
  if (root !== null) {
    scheduleUpdateOnFiber(root, fiber, lane);
  }
}

这个过程正是 React 响应式更新的起点。dispatchAction 将状态更新的意图(action)和优先级(lane)封装成一个 Update 对象,然后启动 React 的更新调度流程。

通过 mountStateupdateStatedispatchAction 的协同工作,useState 为函数组件提供了一个看似简单但功能强大的状态管理机制。它完美地融入了 Fiber 架构和 Lanes 优先级模型,使得状态更新可以被中断、恢复和赋予不同的优先级。

Last updated: