Appearance
手写useState实现
学习目标
通过本章学习,你将深入理解:
- useState的内部工作原理
- Hook状态的存储机制
- 更新队列的处理流程
- 批量更新的实现
- 函数式更新的原理
- useState与Fiber的关系
- 手写简化版useState
- React 19的优化实现
第一部分:useState基础回顾
1.1 useState的使用
jsx
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
<button onClick={() => setCount(c => c + 1)}>+1 (函数式)</button>
</div>
);
}1.2 useState的特点
jsx
// 1. 状态持久化 - 状态在重新渲染间保持
// 2. 触发重渲染 - 调用setState会触发组件重新渲染
// 3. 异步更新 - setState是异步的
// 4. 批量更新 - 多个setState会被批处理
// 5. 函数式更新 - 支持基于前一个state更新第二部分:核心数据结构
2.1 Hook对象结构
javascript
// useState创建的Hook对象
type Hook = {
// 存储当前state值
memoizedState: any,
// 基础state(用于优先级更新)
baseState: any,
// 基础更新队列
baseQueue: Update<any> | null,
// 更新队列
queue: UpdateQueue<any> | null,
// 指向下一个Hook
next: Hook | null
};
// 更新队列结构
type UpdateQueue<S> = {
// 待处理的更新(环形链表)
pending: Update<S> | null,
// dispatch函数(即setState)
dispatch: Dispatch<S> | null,
// 最后渲染的reducer(对于useState就是basicStateReducer)
lastRenderedReducer: ((S, A) => S) | null,
// 最后渲染的state
lastRenderedState: S | null
};
// 单个更新对象
type Update<S> = {
// 更新的action(新值或函数)
action: S | ((prevState: S) => S),
// 下一个更新(环形链表)
next: Update<S>
};2.2 全局变量
javascript
// React维护的全局变量
let currentlyRenderingFiber = null; // 当前渲染的Fiber节点
let currentHook = null; // current树的Hook指针
let workInProgressHook = null; // workInProgress树的Hook指针第三部分:mountState实现
3.1 初始化阶段
javascript
// 简化的mountState实现
function mountState(initialState) {
// 1. 创建Hook对象
const hook = mountWorkInProgressHook();
// 2. 处理初始state
if (typeof initialState === 'function') {
// 惰性初始化
initialState = initialState();
}
hook.memoizedState = hook.baseState = initialState;
// 3. 创建更新队列
const queue = {
pending: null,
dispatch: null,
lastRenderedReducer: basicStateReducer,
lastRenderedState: initialState
};
hook.queue = queue;
// 4. 创建dispatch函数
const dispatch = dispatchSetState.bind(
null,
currentlyRenderingFiber,
queue
);
queue.dispatch = dispatch;
// 5. 返回state和dispatch
return [hook.memoizedState, dispatch];
}
// 创建并挂载Hook
function mountWorkInProgressHook() {
const hook = {
memoizedState: null,
baseState: null,
baseQueue: null,
queue: null,
next: null
};
if (workInProgressHook === null) {
// 第一个Hook,挂载到Fiber
currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
} else {
// 后续Hook,添加到链表末尾
workInProgressHook = workInProgressHook.next = hook;
}
return workInProgressHook;
}
// 基础state reducer
function basicStateReducer(state, action) {
return typeof action === 'function' ? action(state) : action;
}3.2 完整示例
javascript
// 示例:两个useState的初始化
function Component() {
const [count, setCount] = useState(0);
const [name, setName] = useState('Alice');
return <div>{count} - {name}</div>;
}
// 执行流程:
// 1. 调用useState(0)
// → mountState(0)
// → 创建Hook1 { memoizedState: 0, queue: {...}, next: null }
// → Fiber.memoizedState = Hook1
// → 返回 [0, dispatch1]
// 2. 调用useState('Alice')
// → mountState('Alice')
// → 创建Hook2 { memoizedState: 'Alice', queue: {...}, next: null }
// → Hook1.next = Hook2
// → 返回 ['Alice', dispatch2]
// Fiber结构:
// Fiber.memoizedState → Hook1 → Hook2 → null第四部分:dispatchSetState实现
4.1 dispatch函数
javascript
function dispatchSetState(fiber, queue, action) {
// 1. 创建update对象
const update = {
action,
next: null
};
// 2. 将update添加到队列(环形链表)
const pending = queue.pending;
if (pending === null) {
// 第一个update,指向自己形成环
update.next = update;
} else {
// 插入到环形链表中
update.next = pending.next;
pending.next = update;
}
queue.pending = update;
// 3. 计算新状态(急切更新优化)
const lastRenderedReducer = queue.lastRenderedReducer;
if (lastRenderedReducer !== null) {
try {
const currentState = queue.lastRenderedState;
const eagerState = lastRenderedReducer(currentState, action);
// 如果状态没变,不触发更新
if (Object.is(eagerState, currentState)) {
return;
}
} catch (error) {
// 忽略错误,继续正常流程
}
}
// 4. 调度更新
scheduleUpdateOnFiber(fiber);
}
// 简化的调度函数
function scheduleUpdateOnFiber(fiber) {
// 标记fiber需要更新
markUpdateLaneFromFiberToRoot(fiber);
// 调度渲染
ensureRootIsScheduled(fiber.root);
}4.2 更新队列示例
javascript
// 示例:多次调用setState
function Component() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1); // update1
setCount(count + 1); // update2
setCount(c => c + 1); // update3
};
return <button onClick={handleClick}>{count}</button>;
}
// 点击后的更新队列(环形链表):
// queue.pending → update3
// ↓
// update1 ← update2 ←┘
// ↓_________________↑
// 队列遍历:
// 从update3.next开始:update1 → update2 → update3第五部分:updateState实现
5.1 更新阶段
javascript
function updateState(initialState) {
return updateReducer(basicStateReducer, initialState);
}
function updateReducer(reducer, initialArg) {
// 1. 获取对应的Hook
const hook = updateWorkInProgressHook();
const queue = hook.queue;
queue.lastRenderedReducer = reducer;
// 2. 获取current Hook
const current = currentHook;
// 3. 处理更新队列
const pendingQueue = queue.pending;
if (pendingQueue !== null) {
// 有待处理的更新
// 3.1 获取第一个update
const first = pendingQueue.next;
let newState = current.memoizedState;
let update = first;
// 3.2 遍历更新链表,计算新state
do {
const action = update.action;
newState = reducer(newState, action);
update = update.next;
} while (update !== first);
// 3.3 清空pending队列
queue.pending = null;
// 3.4 更新Hook状态
hook.memoizedState = newState;
hook.baseState = newState;
queue.lastRenderedState = newState;
}
// 4. 返回state和dispatch
const dispatch = queue.dispatch;
return [hook.memoizedState, dispatch];
}
// 获取并更新workInProgress Hook
function updateWorkInProgressHook() {
// 从current树获取对应Hook
let nextCurrentHook;
if (currentHook === null) {
const current = currentlyRenderingFiber.alternate;
nextCurrentHook = current.memoizedState;
} else {
nextCurrentHook = currentHook.next;
}
// 移动current指针
currentHook = nextCurrentHook;
// 创建workInProgress Hook
const newHook = {
memoizedState: currentHook.memoizedState,
baseState: currentHook.baseState,
baseQueue: currentHook.baseQueue,
queue: currentHook.queue,
next: null
};
// 添加到workInProgress链表
if (workInProgressHook === null) {
currentlyRenderingFiber.memoizedState = workInProgressHook = newHook;
} else {
workInProgressHook = workInProgressHook.next = newHook;
}
return workInProgressHook;
}5.2 更新流程示例
javascript
// 完整的更新流程
function Component() {
const [count, setCount] = useState(0);
// 用户点击,执行setCount(5)
return <button onClick={() => setCount(5)}>{count}</button>;
}
// 执行流程:
// 1. 点击按钮
// ↓
// 2. setCount(5)
// ↓
// 3. dispatchSetState(fiber, queue, 5)
// ↓
// 4. 创建update: { action: 5, next: ... }
// ↓
// 5. 添加到queue.pending
// ↓
// 6. scheduleUpdateOnFiber(fiber)
// ↓
// 7. 组件重新渲染
// ↓
// 8. 调用useState(0)
// ↓
// 9. updateState(0)
// ↓
// 10. 从queue.pending获取update
// ↓
// 11. 计算新state: basicStateReducer(0, 5) = 5
// ↓
// 12. hook.memoizedState = 5
// ↓
// 13. 返回 [5, dispatch]
// ↓
// 14. 渲染显示5第六部分:批量更新实现
6.1 批处理机制
javascript
// 批处理上下文
let isBatchingUpdates = false;
const updateQueue = [];
function batchedUpdates(fn) {
const prevIsBatching = isBatchingUpdates;
isBatchingUpdates = true;
try {
return fn();
} finally {
isBatchingUpdates = prevIsBatching;
if (!isBatchingUpdates && updateQueue.length > 0) {
// 批量处理所有更新
flushUpdates();
}
}
}
function dispatchSetState(fiber, queue, action) {
const update = {
action,
next: null
};
// 添加到queue
enqueueUpdate(queue, update);
if (isBatchingUpdates) {
// 批处理中,只收集更新
updateQueue.push(fiber);
} else {
// 立即调度更新
scheduleUpdateOnFiber(fiber);
}
}
function flushUpdates() {
// 批量处理收集的更新
const fibers = new Set(updateQueue);
updateQueue.length = 0;
fibers.forEach(fiber => {
scheduleUpdateOnFiber(fiber);
});
}
// React事件处理器自动批处理
function dispatchEvent(event) {
// React合成事件自动包裹batchedUpdates
batchedUpdates(() => {
// 执行用户的事件处理器
invokeGuardedCallback(event);
});
}6.2 批处理示例
javascript
function Component() {
const [count, setCount] = useState(0);
const [flag, setFlag] = useState(false);
const handleClick = () => {
// React 18+: 自动批处理
setCount(c => c + 1); // 不会立即重渲染
setFlag(f => !f); // 不会立即重渲染
setCount(c => c + 1); // 不会立即重渲染
// 批量处理后只触发一次重渲染
};
const handleAsync = () => {
setTimeout(() => {
// React 18+: 异步也会批处理
setCount(c => c + 1);
setFlag(f => !f);
// 只触发一次重渲染
}, 1000);
};
return (
<div>
<p>{count} - {flag ? 'true' : 'false'}</p>
<button onClick={handleClick}>同步更新</button>
<button onClick={handleAsync}>异步更新</button>
</div>
);
}第七部分:简化版实现
7.1 完整的简化实现
javascript
// 全局变量
let currentFiber = null;
let hookIndex = 0;
// 简化的useState实现
function useState(initialState) {
const fiber = currentFiber;
const hooks = fiber.hooks || (fiber.hooks = []);
const index = hookIndex++;
// 初始化Hook
if (!hooks[index]) {
hooks[index] = {
state: typeof initialState === 'function'
? initialState()
: initialState,
queue: []
};
}
const hook = hooks[index];
// 处理更新队列
hook.queue.forEach(action => {
hook.state = typeof action === 'function'
? action(hook.state)
: action;
});
hook.queue = [];
// setState函数
const setState = (action) => {
hook.queue.push(action);
scheduleRender(fiber);
};
return [hook.state, setState];
}
// 调度渲染
function scheduleRender(fiber) {
// 简化版:立即重新渲染
requestIdleCallback(() => {
hookIndex = 0; // 重置Hook索引
currentFiber = fiber;
fiber.render(); // 重新执行组件函数
});
}
// 使用示例
function createComponent(renderFn) {
const fiber = {
hooks: null,
render: () => {
hookIndex = 0;
currentFiber = fiber;
return renderFn();
}
};
return fiber;
}
// 组件
const CounterFiber = createComponent(() => {
const [count, setCount] = useState(0);
const [name, setName] = useState('Alice');
console.log(`Render: count=${count}, name=${name}`);
return {
count,
name,
increment: () => setCount(c => c + 1),
changeName: () => setName('Bob')
};
});
// 初始渲染
let result = CounterFiber.render();
console.log(result); // { count: 0, name: 'Alice', ... }
// 更新
result.increment();
// 触发重渲染,输出:Render: count=1, name=Alice
result.changeName();
// 触发重渲染,输出:Render: count=1, name=Bob7.2 支持批处理的版本
javascript
// 批处理版本
let updateQueue = [];
let isBatching = false;
function batchedUpdates(fn) {
isBatching = true;
fn();
isBatching = false;
// 批量处理更新
if (updateQueue.length > 0) {
const fibers = [...new Set(updateQueue)];
updateQueue = [];
fibers.forEach(fiber => fiber.render());
}
}
function scheduleRender(fiber) {
if (isBatching) {
updateQueue.push(fiber);
} else {
requestIdleCallback(() => {
hookIndex = 0;
currentFiber = fiber;
fiber.render();
});
}
}
// 使用
batchedUpdates(() => {
result.increment(); // 不立即渲染
result.increment(); // 不立即渲染
result.changeName(); // 不立即渲染
// 退出batchedUpdates后,批量处理,只渲染一次
});注意事项
1. 状态不可变
javascript
// ❌ 错误:直接修改state
const [user, setUser] = useState({ name: 'Alice' });
user.name = 'Bob'; // 不会触发更新
setUser(user); // 引用相同,不会重渲染
// ✅ 正确:创建新对象
setUser({ ...user, name: 'Bob' });2. 异步更新
javascript
// setState是异步的
const [count, setCount] = useState(0);
setCount(1);
console.log(count); // 仍然是0
// 使用函数式更新访问最新值
setCount(c => {
console.log(c); // 最新值
return c + 1;
});3. 批处理
javascript
// React自动批处理
function handleClick() {
setCount(c => c + 1); // 更新1
setCount(c => c + 1); // 更新2
// 只触发一次渲染
}常见问题
Q1: 为什么setState是异步的?
A:
- 性能优化:批量处理多个更新
- 一致性:保证props和state的一致性
- 未来优化:支持异步渲染
Q2: 函数式更新和直接更新的区别?
A:
javascript
// 直接更新:依赖当前闭包中的state
setCount(count + 1);
// 函数式更新:总是使用最新的state
setCount(c => c + 1);Q3: 如何实现立即更新?
A:
javascript
// React没有提供立即更新的API
// 但可以使用flushSync(慎用)
import { flushSync } from 'react-dom';
flushSync(() => {
setCount(1);
});
console.log(count); // 1总结
useState实现要点
- 数据结构:Hook对象、更新队列、环形链表
- 初始化:mountState创建Hook并挂载到Fiber
- 更新:updateState处理更新队列
- 调度:dispatchSetState触发组件重渲染
- 批处理:自动批量处理多个更新
- 优化:急切更新、对象比较
核心流程
useState(0)
↓
首次: mountState
↓
创建Hook对象
↓
返回 [state, setState]
↓
setState(newValue)
↓
创建update对象
↓
添加到更新队列
↓
调度渲染
↓
组件重渲染
↓
updateState
↓
处理更新队列
↓
计算新state
↓
返回 [newState, setState]理解useState的实现原理,有助于我们更好地使用它,避免常见陷阱,写出高质量的React代码!