实现 useState Hook
手写一个简易版 React useState Hook
问题
用原生 JavaScript 实现 React 的 useState Hook,支持 state 读取和 setState 更新功能。
解答
基础版本
// 存储所有 state 的数组
let states = [];
// 当前 state 的索引
let stateIndex = 0;
function useState(initialValue) {
// 闭包保存当前索引
const currentIndex = stateIndex;
// 初始化 state(只在首次调用时生效)
if (states[currentIndex] === undefined) {
states[currentIndex] = initialValue;
}
// setState 函数
const setState = (newValue) => {
// 支持函数式更新
if (typeof newValue === 'function') {
states[currentIndex] = newValue(states[currentIndex]);
} else {
states[currentIndex] = newValue;
}
// 触发重新渲染
render();
};
// 索引递增,为下一个 useState 准备
stateIndex++;
return [states[currentIndex], setState];
}
// 重置索引,模拟组件重新渲染
function resetIndex() {
stateIndex = 0;
}
完整示例
let states = [];
let stateIndex = 0;
function useState(initialValue) {
const currentIndex = stateIndex;
if (states[currentIndex] === undefined) {
states[currentIndex] = initialValue;
}
const setState = (newValue) => {
if (typeof newValue === 'function') {
states[currentIndex] = newValue(states[currentIndex]);
} else {
states[currentIndex] = newValue;
}
render();
};
stateIndex++;
return [states[currentIndex], setState];
}
// 模拟组件
function Component() {
const [count, setCount] = useState(0);
const [name, setName] = useState('React');
console.log(`count: ${count}, name: ${name}`);
return { count, setCount, name, setName };
}
// 模拟渲染函数
function render() {
stateIndex = 0; // 重置索引
Component();
}
// 测试
const { setCount, setName } = Component();
setCount(1); // 输出: count: 1, name: React
setCount(n => n + 1); // 输出: count: 2, name: React
setName('Vue'); // 输出: count: 2, name: Vue
使用 Map 的版本(更接近真实实现)
// 模拟 Fiber 节点
const fiber = {
memoizedState: null, // 链表头
stateNode: null,
};
let workInProgressHook = null;
function useState(initialValue) {
const hook = {
memoizedState: initialValue,
next: null,
};
if (!fiber.memoizedState) {
// 第一个 hook
fiber.memoizedState = hook;
} else {
// 追加到链表末尾
workInProgressHook.next = hook;
}
workInProgressHook = hook;
const setState = (newValue) => {
hook.memoizedState = typeof newValue === 'function'
? newValue(hook.memoizedState)
: newValue;
render();
};
return [hook.memoizedState, setState];
}
关键点
- 闭包保存索引:每个
setState通过闭包记住自己对应的 state 索引 - 调用顺序固定:Hook 必须在组件顶层调用,不能在条件语句中使用,否则索引会错乱
- 重渲染时重置索引:每次渲染前将
stateIndex归零,保证 Hook 按顺序匹配 - 支持函数式更新:
setState(prev => prev + 1)可以获取最新状态 - 真实 React 用链表:实际实现使用链表结构存储 Hook,挂载在 Fiber 节点上
目录