React useState 源码深度解析
前言
useState 是 React Hooks 中最常用的 Hook 之一。本文将深入源码,揭示 useState 的实现原理和工作机制。
目录
基础用法
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
);
}
源码结构
源码位置
在 React 源码中,Hooks 相关代码主要在以下位置:
react/
├── packages/
│ ├── react/
│ │ └── src/
│ │ └── ReactHooks.js # useState 入口
│ └── react-reconciler/
│ └── src/
│ ├── ReactFiberHooks.js # Hooks 核心实现
│ └── ReactFiberWorkLoop.js # 调度逻辑
入口代码
// packages/react/src/ReactHooks.js
export function useState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
const dispatcher = resolveDispatcher();
return dispatcher.useState(initialState);
}
核心原理
1. Dispatcher 机制
React 使用 Dispatcher 模式来管理 Hooks。根据组件所处的不同阶段,会使用不同的 dispatcher:
// packages/react-reconciler/src/ReactFiberHooks.js
// 首次渲染时的 dispatcher
const HooksDispatcherOnMount = {
useState: mountState,
useEffect: mountEffect,
// ...
};
// 更新时的 dispatcher
const HooksDispatcherOnUpdate = {
useState: updateState,
useEffect: updateEffect,
// ...
};
2. Hook 数据结构
每个 Hook 会创建一个 hook 对象:
type Hook = {
memoizedState: any, // 当前状态值
baseState: any, // 基础状态
baseQueue: Update<any>, // 基础更新队列
queue: UpdateQueue<any>, // 更新队列
next: Hook | null, // 指向下一个 Hook(链表结构)
};
3. Fiber 节点与 Hooks 链表
每个 Fiber 节点上维护着一个 Hooks 链表:
// Fiber 节点结构(简化)
type Fiber = {
memoizedState: Hook | null, // 指向第一个 Hook
updateQueue: any,
// ...
};
链表结构示意:
Fiber.memoizedState
↓
Hook1 (useState) → Hook2 (useEffect) → Hook3 (useState) → null
首次渲染流程
mountState 实现
// packages/react-reconciler/src/ReactFiberHooks.js
function mountState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
// 1. 创建 Hook 对象并加入链表
const hook = mountWorkInProgressHook();
// 2. 处理初始状态(支持函数)
if (typeof initialState === 'function') {
initialState = initialState();
}
// 3. 保存初始状态
hook.memoizedState = hook.baseState = initialState;
// 4. 创建更新队列
const queue = (hook.queue = {
pending: null,
dispatch: null,
lastRenderedReducer: basicStateReducer,
lastRenderedState: initialState,
});
// 5. 创建 dispatch 函数(绑定当前 Fiber 和 queue)
const dispatch = (queue.dispatch = dispatchSetState.bind(
null,
currentlyRenderingFiber,
queue,
));
// 6. 返回 [state, setState]
return [hook.memoizedState, dispatch];
}
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 {
// 后续 Hook,加入链表尾部
workInProgressHook = workInProgressHook.next = hook;
}
return workInProgressHook;
}
更新流程
updateState 实现
function updateState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
// updateState 实际上调用 updateReducer
return updateReducer(basicStateReducer, initialState);
}
updateReducer 核心逻辑
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;
// 2. 获取更新队列
const pending = queue.pending;
if (pending !== null) {
// 3. 处理所有待处理的更新
const first = pending.next;
let newState = hook.baseState;
let update = first;
do {
// 计算新状态
const action = update.action;
newState = reducer(newState, action);
update = update.next;
} while (update !== first);
// 4. 更新 Hook 状态
hook.memoizedState = newState;
hook.baseState = newState;
queue.pending = null;
}
// 5. 返回新状态和 dispatch
const dispatch = queue.dispatch;
return [hook.memoizedState, dispatch];
}
basicStateReducer
function basicStateReducer<S>(state: S, action: BasicStateAction<S>): S {
// 支持函数式更新:setState(prev => prev + 1)
return typeof action === 'function' ? action(state) : action;
}
Dispatch 函数:dispatchSetState
function dispatchSetState<S, A>(
fiber: Fiber,
queue: UpdateQueue<S, A>,
action: A,
) {
// 1. 创建 update 对象
const update: Update<S, A> = {
action,
next: null,
};
// 2. 将 update 加入队列(环形链表)
const pending = queue.pending;
if (pending === null) {
// 第一个更新,指向自己形成环
update.next = update;
} else {
update.next = pending.next;
pending.next = update;
}
queue.pending = update;
// 3. 调度更新(触发重新渲染)
scheduleUpdateOnFiber(fiber);
}
Fiber 架构中的 Hooks
为什么 Hooks 必须按顺序调用?
因为 React 依赖 调用顺序 来匹配 Hooks 与 Fiber 节点上的链表:
// ❌ 错误:条件调用会打乱顺序
function Component() {
if (condition) {
const [a, setA] = useState(0); // 有时是第一个,有时不存在
}
const [b, setB] = useState(0); // 位置不稳定
}
// ✅ 正确:顺序固定
function Component() {
const [a, setA] = useState(0); // 始终是第一个
const [b, setB] = useState(0); // 始终是第二个
}
Hooks 链表遍历
function updateWorkInProgressHook(): Hook {
// 从 Fiber 的 memoizedState 链表中取出对应 Hook
if (currentHook === null) {
const current = currentlyRenderingFiber.alternate;
currentHook = current.memoizedState;
} else {
currentHook = currentHook.next;
}
// 复制到 workInProgress 树
const newHook: Hook = {
memoizedState: currentHook.memoizedState,
baseState: currentHook.baseState,
baseQueue: currentHook.baseQueue,
queue: currentHook.queue,
next: null,
};
// 加入新的链表
if (workInProgressHook === null) {
workInProgressHook = newHook;
currentlyRenderingFiber.memoizedState = newHook;
} else {
workInProgressHook = workInProgressHook.next = newHook;
}
return workInProgressHook;
}
常见问题解析
1. 为什么 setState 是异步的?
function Component() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
console.log(count); // 仍然是旧值!
};
}
原因:
setState只是将更新加入队列,不会立即修改状态- React 会批量处理更新(batch updates),在合适时机统一重新渲染
- 优化性能,避免不必要的多次渲染
解决方案:
// 使用函数式更新保证基于最新值
setCount(prevCount => prevCount + 1);
2. 为什么多次 setState 只生效一次?
const handleClick = () => {
setCount(count + 1); // count = 0 => 1
setCount(count + 1); // count = 0 => 1(仍基于旧值)
setCount(count + 1); // count = 0 => 1
};
// 结果:count = 1
原因:
- 三次
setCount都是基于渲染时的闭包值count = 0 - 等价于
setCount(1)执行了三次
解决方案:
const handleClick = () => {
setCount(c => c + 1); // 0 => 1
setCount(c => c + 1); // 1 => 2
setCount(c => c + 1); // 2 => 3
};
// 结果:count = 3
3. useState 的初始值只执行一次?
function Component() {
const [state, setState] = useState(expensiveCalculation());
// expensiveCalculation 每次渲染都会执行!
}
// ✅ 惰性初始化
function Component() {
const [state, setState] = useState(() => expensiveCalculation());
// 只在首次渲染时执行一次
}
4. 对象/数组更新不生效?
// ❌ 错误:直接修改原对象
const [obj, setObj] = useState({ count: 0 });
obj.count = 1;
setObj(obj); // React 检测不到变化(引用相同)
// ✅ 正确:创建新对象
setObj({ ...obj, count: 1 });
总结
useState 核心要点
- Dispatcher 模式:根据渲染阶段选择不同的实现
- 链表结构:多个 Hooks 通过链表连接在 Fiber 节点上
- 更新队列:setState 将更新加入环形链表,批量处理
- 顺序依赖:依赖调用顺序匹配 Hook,不能条件调用
- 函数式更新:支持
setState(prev => next)避免闭包陷阱
实现流程图
用户调用 useState(0)
↓
首次渲染:mountState
↓
创建 Hook 对象 → 加入链表 → 创建 dispatch 函数
↓
返回 [state, setState]
↓
用户调用 setState(1)
↓
dispatchSetState:创建 update → 加入队列 → 触发调度
↓
重新渲染:updateState
↓
updateReducer:处理队列 → 计算新状态 → 返回新值
参考资料
作者: 哈基米 (部署小龙虾让ai写的文章)
日期: 2026-03-11
关键词: React, useState, Hooks, 源码分析, Fiber