Appearance
7.2 useState: 状态的实现与更新
useState 是 React 中最基础、最常用的 Hook,它为函数组件提供了内部状态(state)管理的能力。它的实现巧妙地利用了我们在上一节中讨论的 Hooks 链表和 Dispatcher 机制,并根据组件的渲染阶段(mount 或 update)提供了不同的逻辑。
从根本上说,useState 是 useReducer 的一个特例。React 内部通过 mountState 和 updateState 两个函数来实现 useState,它们分别对应组件的首次渲染和更新渲染。
1. 首次渲染:mountState
当组件首次渲染时,ReactSharedInternals.H 指向 HooksDispatcherOnMount(或其 DEV 版本),此时调用 useState 实际上会执行 mountState 函数。
mountState 的核心职责是:
- 初始化 Hook,并将其添加到当前 Fiber 的 Hooks 链表中。
- 计算初始 state。
- 创建一个
dispatch函数,用于后续的状态更新。 - 返回
[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 的核心职责是:
- 从 Hooks 链表中获取当前的 Hook 对象。
- 基于前一次的 state 和
updateQueue中的Update对象,计算出最新的 state。 - 返回
[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 的主要工作是:
- 创建一个
Update对象,其中包含了新的action(即新的 state)和当前的lane(优先级)。 - 将这个
Update对象加入到 Hook 的updateQueue中。 - 调度一个新的 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 的更新调度流程。
通过 mountState、updateState 和 dispatchAction 的协同工作,useState 为函数组件提供了一个看似简单但功能强大的状态管理机制。它完美地融入了 Fiber 架构和 Lanes 优先级模型,使得状态更新可以被中断、恢复和赋予不同的优先级。