🧠 01|useState 本质:React 给你分配"状态槽位"
useState 会在当前组件的 Fiber 上分配一个 Hook 节点,用来存储状态和更新队列。每次渲染中,React 按固定顺序读取这些节点,因此你总能拿到正确的最新 state。
核心关键点如下👇
1. 函数组件每次渲染都是"重新执行"
组件里写的:
scss
const [count, setCount] = useState(0);
每次渲染都会执行一次。但 state 不存放在函数里,而存在 Fiber 结构里。
2. 每个组件 Fiber 都维护一个 hooks 链表
可以想象成这样:
scss
Fiber
└─ memoizedState
├─ HookNode1 (对应第一个 useState)
├─ HookNode2 (对应第二个 useState)
└─ ...
React 每次调用 Hook 时,会沿着链表走一个节点。
3. setState 只是把更新"入队"
每个 HookNode 都有个 update queue:
arduino
queue: [update1, update2, ...]
下一次渲染前,React 会把这些更新依次应用,然后再返回最新值。
4. 不能在条件中使用hook
因为每次渲染 React 都是按顺序"走链表":
第一次:
css
useState A → 节点1
useState B → 节点2
第二次如果你写了条件分支导致 B 不执行:
css
useState A → 节点1
React 就会把节点2 的状态错读为节点1,状态直接乱套。
Hook 顺序必须固定才能保证取到正确的 state。
🔍 02|useState 的参数与返回值(TypeScript 思维)
php
function useState<S>(
initialState: S | (() => S)
): [S, (update: S | ((prev: S) => S)) => void]
- initialState 支持懒初始化函数(只在首次执行)
- setState 支持两种更新方式:直接值、更新函数
① 直接值
scss
const [count, setCount] = useState(0);
- 类型是你传入的值类型,比如
0→ number,"abc"→ string。 - 每次组件重新渲染时,这个值不会再被计算一次。
- 如果初始值计算开销大,每次渲染直接写计算式可能性能不佳。
② 懒初始化函数(lazy initializer)
scss
const [data, setData] = useState(() => expensiveComputation());
- 函数只在第一次渲染调用,返回值作为初始 state。
- 好处:延迟计算开销,避免每次渲染都执行复杂逻辑。
🧩 03|state 的特点
自动批处理
React 会自动批处理(batch),多个 setState → 只渲染一次(视版本和环境而定)
替换非合并
useState 是"替换",不像 class setState 那样"合并"
对象必须手动展开:
ini
setForm(prev => ({ ...prev, name: 'xx' }));
函数式更新可以避免读到旧值
ini
setCount(c => c + 1);
📦 04|mini-hooks
ini
let currentFiber = null;
// 渲染入口
function render(component) {
currentFiber = component.fiber;
currentFiber.hookIndex = 0;
if (!currentFiber.hooks) {
currentFiber.hooks = [];
currentFiber.isMount = true;
} else {
currentFiber.isMount = false;
}
const result = component();
console.log("Rendered:", result);
return result;
}
// 创建组件(带 fiber)
function createComponent(fn) {
fn.fiber = { hooks: null, hookIndex: 0 };
return fn;
}
function useState(initialState) {
const fiber = currentFiber;
const hookIndex = fiber.hookIndex++;
if (fiber.isMount) {
const hook = {
memoizedState:
typeof initialState === "function" ? initialState() : initialState,
queue: [],
};
fiber.hooks.push(hook);
}
const hook = fiber.hooks[hookIndex];
hook.queue.forEach((update) => {
const prev = hook.memoizedState;
hook.memoizedState =
typeof update === "function" ? update(prev) : update;
});
hook.queue = [];
const setState = (action) => {
hook.queue.push(action);
rerender();
};
return [hook.memoizedState, setState];
}
// 调度器
let rerenderScheduled = false;
function rerender() {
if (!rerenderScheduled) {
rerenderScheduled = true;
setTimeout(() => {
rerenderScheduled = false;
render(App);
}, 0);
}
}
const App = createComponent(function App() {
const [count, setCount] = useState(0);
const [text, setText] = useState("hello");
return {
count,
text,
inc: () => setCount((c) => c + 1),
changeText: () => setText((t) => t + "!"),
};
});
// 首次渲染
const app = render(App);
// 调用更新
app.inc();
app.inc();
app.changeText();