← 返回目录

第 19 卷

第 19 / 28 卷日期:

12}, []);

13 14return <div>定时器示例</div>;

15}

3. 规则检测工具

为了避免违反 Hooks 规则,推荐使用 ESLint 插件 eslint-plugin-react-hooks,该插件会自动检测代码中 Hooks 的使用情况,对违反规则的代码给出警告/错误提示。

配置方式(在 .eslintrc.js 中):

Code block

1 2module.exports = {

3plugins: ['react-hooks'], 4rules: {

5//检查Hooks的调用规则(顶层调用、仅在React函数中调用)
6'react-hooks/rules-of-hooks': 'error',
7//检查依赖数组的完整性
8'react-hooks/exhaustive-deps': 'warn'
9}
10 };

二十九、请解释 React 如何追踪 Hook 的状态?Hooks 的原理是什么?

React Hooks 的核心原理是「通过链表结构追踪 Hooks 状态,结合 Fiber 架构维护组件状态与 Hooks 的关联」。React 并非通过“命名”或“变量名”追踪状态,而是通过「Hooks 的调用顺序」

和「组件的 Fiber 节点」来精准关联每个 Hook 及其对应的状态,确保组件重新渲染时 Hooks 能正确读取/更新状态。

  1. React 如何追踪 Hook 的状态?

React 追踪 Hook 状态的核心依赖两个关键机制:「Fiber 节点的 Hooks 链表」和「Hooks 调用顺序的稳定性」。

(1)Fiber 节点:存储组件状态的容器

React 16+ 采用 Fiber 架构,每个组件对应一个「Fiber 节点」(虚拟 DOM 的升级版本,用于描述组

(2)Hooks 链表:按调用顺序串联状态

当函数组件首次渲染时,React 会按 Hooks 的调用顺序,创建对应的 Hook 节点,并将这些节点串联成一个单向链表,最终将链表头节点赋值给组件 Fiber 节点的 memoizedState。

每个 Hook 节点包含以下核心信息(以 useState 为例):

  • memoizedState:当前 Hook 的状态值(如 useState 的 count 值);
  • queue:状态更新队列,存储该 Hook 的待处理更新(如 setState 触发的更新);
  • next:指向链表中的下一个 Hook 节点(串联所有 Hooks)。

示例:首次渲染时的 Hooks 链表创建过程

Code block

1 2// 函数组件

3function MyComponent() {

4const [count, setCount] = useState(0); // Hook 1
5const [name, setName] = useState(''); // Hook 2
6useEffect(() => { ... }, [count]); // Hook 3
7return <div>...</div>;
8}

9 10// 首次渲染时,React 执行流程:

  1. 初始化 Fiber 节点的 memoizedState 为 null;
  2. 执行 useState(0):创建 Hook1 节点(memoizedState=0),Fiber.memoizedState 指向

Hook1;

  1. 执行 useState(''):创建 Hook2 节点(memoizedState=''),Hook1.next 指向 Hook2;
  2. 执行 useEffect(...):创建 Hook3 节点(存储依赖 [count]、回调函数等),Hook2.next

指向 Hook3;

  1. 最终形成链表:Fiber.memoizedState → Hook1 → Hook2 → Hook3。

(3)重新渲染时:按顺序遍历链表读取状态

当组件因状态变化、props 变化等触发重新渲染时,React 会:

  1. 获取组件 Fiber 节点的 memoizedState(即 Hooks 链表头节点);
  2. 按与首次渲染完全相同的顺序,遍历 Hooks 链表:
  3. 执行第一个 useState 时,读取链表头节点(Hook1)的 memoizedState(count 值),并返

回 [count, setCount];

  1. 执行 useEffect 时,找到 Hook3 节点,对比依赖项变化,决定是否执行回调函数;
  2. 遍历完成后,确保所有 Hooks 都能正确读取到对应的状态。

关键结论:React 是通过「Hooks 的调用顺序」来匹配链表中的 Hook 节点,进而追踪状态的。这也是为什么 Hooks 必须在组件顶层调用(保证调用顺序稳定)的核心原因⸺若顺序变化,遍历链表时

会匹配到错误的 Hook 节点,导致状态错乱。

2. Hooks 的核心原理(以 useState 和 useEffect 为例)

(1)useState 的原理

useState 的核心作用是“创建状态并返回更新状态的函数”,其原理可拆解为 3 个步骤:

  1. 首次渲染:创建 Hook 节点并初始化状态:
  2. 当调用 useState(initialState) 时,React 会创建一个 Hook 节点,将

initialState 作为初始状态存入节点的 memoizedState;

  1. 创建状态更新队列(queue),用于存储后续通过 setCount 触发的更新;
  2. 将该 Hook 节点加入组件的 Hooks 链表,并返回 [memoizedState, dispatch](dispatch 是更新

状态的函数,即 setCount)。

  1. 触发更新:将更新加入队列并调度渲染:
  2. 当调用 setCount(newState) 时,实际上是调用 dispatch 函数;
  3. dispatch 函数会将 newState 包装成一个“更新对象”,加入到对应 Hook 节点的 queue 中;
  4. 调用 React 的调度器(Scheduler),触发组件重新渲染。
  5. 重新渲染:合并更新并更新 Hook 节点状态:
  6. 组件重新渲染时,遍历 Hooks 链表找到当前 useState 对应的 Hook 节点;
  7. 合并该节点 queue 中的所有更新(如多次调用 setCount,会合并为最终的新状态);
  8. 将合并后的新状态更新到 Hook 节点的 memoizedState;
  9. 清空更新队列,返回 [新状态, dispatch]。

补充:setCount 是异步的,因为 React 会将更新加入队列后批量处理,避免频繁渲染提升性能(React18 后支持自动批处理)。

(2)useEffect 的原理

useEffect 的核心作用是“处理副作用并在合适时机清理”,其原理可拆解为 4 个步骤:

全站分卷索引 · 28 卷
  1. 1第 1 卷
  2. 2第 2 卷
  3. 3第 3 卷
  4. 4第 4 卷
  5. 5第 5 卷
  6. 6第 6 卷
  7. 7第 7 卷
  8. 8第 8 卷
  9. 9第 9 卷
  10. 10第 10 卷
  11. 11第 11 卷
  12. 12第 12 卷
  13. 13第 13 卷
  14. 14第 14 卷
  15. 15第 15 卷
  16. 16第 16 卷
  17. 17第 17 卷
  18. 18第 18 卷
  19. 19第 19 卷
  20. 20第 20 卷
  21. 21第 21 卷
  22. 22第 22 卷
  23. 23第 23 卷
  24. 24第 24 卷
  25. 25第 25 卷
  26. 26第 26 卷
  27. 27第 27 卷
  28. 28第 28 卷