React Hooks
相关源文件
React Hooks 提供了一种在函数组件中使用 state 和其他 React 功能的方式,而无需编写类组件。这个系统是现代 React 开发的核心,它使得在函数组件中实现状态逻辑、副作用、上下文访问和其他基本功能成为可能。
有关 Hooks 所属的整体协调过程的信息,请参阅 React Reconciler。
概述
React Hooks 于 React 16.8 引入,允许开发者使用以前仅在类组件中可用的 React 功能。Hooks 提供了一个更直接的 API 来访问 React 的核心概念,如 state、context 和生命周期行为。
Hooks 是作为 React Fiber 协调架构的一部分实现的。Hooks 系统负责
- 创建和维护渲染之间的组件 state
- 管理副作用(DOM 操作、数据获取、订阅)
- 访问 context 和其他 React 功能
- 通过 memoization 优化渲染性能
来源
图示:Hooks 在 React 架构中的位置
来源
核心 Hooks 架构
Hooks 系统围绕几个关键组件构建
- Hooks Dispatcher:提供基于当前执行上下文的 Hooks 实现的中央机制
- Hooks Storage:存储附加到 fiber 的 Hooks 状态的链表结构
- Hooks Rules:Hooks 状态跟踪所需的严格调用顺序要求
- Effect System:具有不同时间(布局效果、被动效果)的副作用管理
来源
内部 Hooks 状态管理
React 将 Hooks 状态维护为 fiber 的 memoizedState 属性上的链表。组件中的每个 Hook 都会向此列表添加一个节点,这就是为什么 Hooks 在每次渲染时都必须按相同的顺序调用——React 依赖调用顺序将 Hooks 调用与其存储的状态关联起来。
来源
Hooks 执行模型
Hooks 在 React 协调过程的渲染阶段执行。renderWithHooks 函数是组件渲染过程中 Hooks 处理的入口点。
使用 Hooks 进行渲染
带 Hooks 的组件的渲染过程遵循以下流程
- 当协调器处理函数组件时,会调用
renderWithHooks
- 根据是挂载还是更新,设置相应的 Hooks Dispatcher
- 执行组件函数,调用 Hooks
- 每次 Hooks 调用都会更新内部 Hooks 状态并可能调度更新
- 返回组件的渲染结果
来源
Hooks 类型
React 提供了几种内置 Hooks,它们各服务于不同的目的
State Hooks
- useState:提供 state 变量和 setter 函数
- useReducer:提供具有基于 reducer 的更新逻辑的 state
Effect Hooks
- useEffect:在渲染后运行副作用,带有清理功能
- useLayoutEffect:类似于 useEffect,但在 DOM 变异后同步运行
- useInsertionEffect:在任何 DOM 变异(用于 CSS-in-JS 库)之前运行
Context Hooks
- useContext:读取并订阅 React context
Ref Hooks
- useRef:创建一个在渲染之间持久化的可变对象
- useImperativeHandle:自定义通过 ref 暴露的值
- useMemo:memoizes 计算结果
- useCallback:memoizes 回调函数
- useTransition:将 state 更新标记为非紧急
- useDeferredValue:延迟更新 UI 的非关键部分
Other Hooks
- useId:为可访问性生成唯一 ID
- useDebugValue:在 React DevTools 中显示标签
来源
useState 实现
useState Hook 是最常用的 Hook 之一。它提供了组件 state 和用于更新它的函数。
内部流程
来源
状态更新
当调用 useState 的 state setter 时,React 会
- 创建一个包含新 state 或更新函数的更新对象
- 将其添加到 Hooks 的更新队列中
- 调度组件的重新渲染
- 在下一次渲染期间,处理所有挂起的更新以计算新 state
来源
useEffect 系统
useEffect Hook 允许函数组件执行副作用。
Effect Phases
React 中的 Effects 在不同的阶段执行
- Layout Effects:在所有 DOM 变异之后、浏览器绘制之前同步运行
- Passive Effects:在浏览器绘制后异步运行
来源
Effect 生命周期
当使用 useEffect 的组件渲染时
- 在渲染期间,捕获 effect 函数和依赖项
- 在 commit 期间,React 会将依赖项与之前的依赖项进行比较
- 如果依赖项已更改(或这是首次渲染),则调用之前的 effect 的清理函数
- 新的 effect 将被安排在浏览器绘制后运行
来源
Hooks 规则
Hooks 系统要求严格遵守特定的规则
- 只在顶层调用 Hooks——不在循环、条件或嵌套函数中调用
- 只从 React 函数组件或自定义 Hooks 中调用 Hooks——不从常规 JavaScript 函数中调用
这些规则之所以存在,是因为 React 依赖调用顺序将 Hooks 调用与其在渲染之间的状态关联起来。Hooks 实现使用了链表数据结构,并且调用顺序必须保持一致,才能正确地将 Hooks 与其状态关联。
React 提供了 ESLint 规则来自动强制执行这些约束。
来源
自定义 Hooks
自定义 Hooks 是使用 React Hooks 的 JavaScript 函数,它们遵循与内置 Hooks 相同的规则。它们能够提取和重用组件之间的有状态逻辑。
自定义 Hook 模式
自定义 Hooks 遵循与内置 Hooks 相同的执行模型,允许组件重用有状态逻辑,同时为每个组件实例维护独立的状态。
来源
React 提供了专门的开发专用功能来调试 Hooks
- Hook 警告:检测并警告常见的 Hook 错误(顺序更改、规则违规)
- DevTools 集成:在 React DevTools 中显示 Hook 状态和值
- useDebugValue:允许自定义 Hooks 在 DevTools 中显示自定义标签
来源
Hooks 提供了多种优化来帮助管理组件性能
Memoization Hooks (记忆化 Hooks)
- useMemo:防止昂贵的计算在每次渲染时运行
- useCallback:防止回调函数在每次渲染时更改身份
这些 Hooks 有助于打破可能导致子组件不必要重新渲染的“引用相等”链。
渲染优化
- useTransition:将状态更新标记为非紧急,允许其他更新优先
- useDeferredValue:创建值的延迟版本,该版本会逐渐过渡
来源
幕后:Hook Dispatcher (Hook 分发器)
React 使用分发器机制,根据当前阶段(渲染、挂载、更新等)提供不同的 Hook 实现。分发器会在组件渲染前被切换,决定将调用哪个 Hook 实现。
来源
与 React Fiber 集成
Hooks 与 React Fiber 架构深度集成
- Hook 状态存储在 Fiber 的
memoizedState 属性上
- Hook 更新可以通过 Fiber 工作循环调度工作
- Effects 通过 Fiber 上的 effect tag 系统进行管理
当 Hook 调度更新时,它会
- 创建一个更新对象
- 将其添加到组件的更新队列中
- 通过
scheduleUpdateOnFiber 调度重渲染
- 渲染系统最终会处理这些更新
来源
结论
React Hooks 为管理函数组件中的状态和副作用提供了一个强大的 API。Hooks 系统构建在 React Fiber 架构之上,利用其能力来跟踪组件状态和调度更新。理解 Hooks 的内部结构有助于有效地使用它们并调试可能出现的问题。
Hooks 的规则并非随意限制,而是 Hooks 系统实现方式的直接结果。遵循这些规则可以确保 React 在渲染之间正确地将 Hook 调用与其状态关联起来。