摘要:React 16 的背景Hooks 的设计理念核心 Hooks 的底层实现思路(useState, useEffect, useContext, …)React 内部的 Hook 数据结构执行流程(函数组件渲染时 Hook 的调用顺序)
好的 ,你问的是 React 16 中所有 Hooks 的底层实现和原理。
这里我会分几个层次来解释:
React Hooks 有几个原则:
只能在函数组件顶层调用,不能在条件语句或循环里动态调用。这是因为 Hooks 依赖调用顺序来定位。只能在 React 函数组件或自定义 Hook 中调用。每个组件 fiber 节点维护一个单链表,链表节点就是 Hook 对象。这就是为什么 eslint-plugin-react-hooks 会强制规则:不要在 if/for 中调用 Hook。
本质是 useReducer 的语法糖。内部维护一个 Hook 节点对象(链表的一部分):type Hook = {memoizedState: any, // 当前的 statebaseState: any, // 初始 statequeue: UpdateQueue, // 更新队列(链表结构)next: Hook | null // 指向下一个 Hook}当你调用 useState(initial) 时:创建一个更新对象(action)。挂到 queue 上。触发 scheduleUpdateOnFiber,进入调度。React 在当前 fiber 上创建一个 Hook 节点,memoizedState = initial。返回 [state, dispatch]。dispatch 本质是一个函数:渲染时,React 会执行 processUpdateQueue,消费 queue,更新 memoizedState。保存副作用函数和依赖数组。Hook 节点结构:type Effect = {tag: HookFlags, // effect 类型(Passive, Layout 等)create: => ( => void) | void, // 副作用函数destroy?: => void, // 清理函数deps: any | null, // 依赖next: Effect | null}调用 useEffect(fn, deps) 时:如果 deps 变了,就调用上一次的 destroy(清理),再执行新的 create。如果没变,则跳过。React 在 Hook 节点里记录 create 和 deps。在 commit 阶段(DOM 更新后),React 比较新旧依赖:类似 Redux 的 reducer:useState 底层其实就是 useReducer 的特例:useState(initial) === useReducer((s, a) => typeof a === 'function' ? a(s) : a, initial)保存 reducer 和 state,通过 dispatch 触发更新。维护一个 { current: any } 对象,整个生命周期内保持引用不变。实现:只在 Hook 节点里存一个 memoizedState = { current: initialValue },之后不会更新。在 fiber 节点上,每个函数组件对应一个 Hook 链表:
type Fiber = {memoizedState: Hook | null; // hook 链表头updateQueue: any;// ...}renderWithHooks 函数:进入函数组件渲染时,初始化 currentlyRenderingFiber。每调用一个 Hook,就在链表上走一步,复用或新建节点。保证 “调用顺序一致 = Hook 顺序一致”。初次渲染执行函数组件,依次调用 useState / useEffect …为每个 Hook 创建一个 Hook 节点,挂到 fiber.memoizedState 链表上。更新渲染执行组件时,React 会遍历 fiber.memoizedState 链表。每次调用 Hook,就拿到对应节点,复用之前的状态。如果调用顺序错了(比如把 Hook 放到 if 里),链表错位,React 会报错。提交阶段(commit phase)对于 useEffect / useLayoutEffect,会根据依赖比较来执行副作用。对于 useState / useReducer,会处理更新队列,得到新 state。✅ 总结:
React Hooks 的底层实现是 fiber 节点上的 Hook 链表 + 调用顺序保证。每个 Hook 维护一个小的状态对象,存储 memoizedState、queue、deps 等。useState 基于更新队列;useEffect 延迟到 commit 阶段执行;useRef 只是一个稳定对象;useMemo / useCallback 做依赖比较。来源:小帆科技园地