react的 hooks 是如何存储的
在 React 中,Hooks 的存储与 Fiber 架构紧密相关,其核心机制是通过链表结构将 Hooks 与组件实例关联,并依托 Fiber 节点保存 Hooks 状态。具体实现逻辑如下:
1. Hooks 存储的载体:Fiber 节点
React 中每个组件(函数组件/类组件)都会对应一个 Fiber 节点(虚拟 DOM 的升级版本),用于存储组件的类型、DOM 信息、状态数据等。
对于函数组件,Hooks 的数据就保存在其对应 Fiber 节点的 memoizedState
属性中。
2. Hooks 的存储结构:单向链表
函数组件中定义的多个 Hooks(如 useState
、useEffect
等)会以单向链表的形式存储在 Fiber 节点的 memoizedState
中:
- 每个 Hook 自身是一个对象,包含
state
(当前状态值)、queue
(更新队列)、next
(指向下一个 Hook 的指针)等属性。 - 第一个 Hook 被挂载到 Fiber 节点的
memoizedState
上,后续每个 Hook 通过next
指针与前一个 Hook 连接,形成链表。
例如,以下组件的 Hooks 存储结构:
function MyComponent() {const [name, setName] = useState(''); // Hook 1const [age, setAge] = useState(0); // Hook 2useEffect(() => {}, []); // Hook 3
}
对应的链表结构:
Fiber.memoizedState → Hook1 → Hook2 → Hook3 → null
(每个 Hook 包含自身状态和 next 指针)
3. Hooks 与组件的关联:调用顺序的重要性
React 依靠Hooks 的调用顺序来匹配链表中的对应节点:
- 组件首次渲染时,按顺序执行所有 Hooks,依次创建 Hook 对象并串联成链表,挂载到 Fiber 节点。
- 组件更新时(如状态变化、父组件传参变化),会按完全相同的顺序重新执行 Hooks,通过链表的指针遍历找到对应的 Hook 对象,从而读取/更新状态。
这也是为什么 React 要求Hooks 必须在函数组件顶层调用,不能在条件语句、循环中使用——如果调用顺序改变,会导致链表遍历错位,无法正确匹配状态。
4. 不同类型 Hooks 的存储差异
- 状态类 Hooks(
useState
、useReducer
):在链表节点中保存具体的状态值(state
)和更新队列(queue
,用于存储待执行的更新操作)。 - 副作用 Hooks(
useEffect
、useLayoutEffect
):除了自身状态,还会在链表中保存副作用函数、依赖项数组等,React 会根据依赖项变化决定是否执行副作用。 - 缓存类 Hooks(
useMemo
、useCallback
):存储计算结果或函数引用,依赖项不变时直接复用缓存值。 - 上下文 Hooks(
useContext
):不存储状态,而是通过读取上下文的 Fiber 节点获取最新值。
总结
Hooks 的存储本质是:以单向链表形式将 Hooks 状态挂载到组件对应的 Fiber 节点上,通过固定的调用顺序实现状态的读取与更新。这种设计既保证了函数组件能拥有状态管理能力,又避免了类组件的 this 指向问题,是 React 函数式编程范式的核心实现。