菜单

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 系统负责

  1. 创建和维护渲染之间的组件 state
  2. 管理副作用(DOM 操作、数据获取、订阅)
  3. 访问 context 和其他 React 功能
  4. 通过 memoization 优化渲染性能

来源

图示:Hooks 在 React 架构中的位置

来源

核心 Hooks 架构

Hooks 系统围绕几个关键组件构建

  1. Hooks Dispatcher:提供基于当前执行上下文的 Hooks 实现的中央机制
  2. Hooks Storage:存储附加到 fiber 的 Hooks 状态的链表结构
  3. Hooks Rules:Hooks 状态跟踪所需的严格调用顺序要求
  4. Effect System:具有不同时间(布局效果、被动效果)的副作用管理

来源

内部 Hooks 状态管理

React 将 Hooks 状态维护为 fiber 的 memoizedState 属性上的链表。组件中的每个 Hook 都会向此列表添加一个节点,这就是为什么 Hooks 在每次渲染时都必须按相同的顺序调用——React 依赖调用顺序将 Hooks 调用与其存储的状态关联起来。

来源

Hooks 执行模型

Hooks 在 React 协调过程的渲染阶段执行。renderWithHooks 函数是组件渲染过程中 Hooks 处理的入口点。

使用 Hooks 进行渲染

带 Hooks 的组件的渲染过程遵循以下流程

  1. 当协调器处理函数组件时,会调用 renderWithHooks
  2. 根据是挂载还是更新,设置相应的 Hooks Dispatcher
  3. 执行组件函数,调用 Hooks
  4. 每次 Hooks 调用都会更新内部 Hooks 状态并可能调度更新
  5. 返回组件的渲染结果

来源

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 暴露的值

Performance Hooks

  • useMemo:memoizes 计算结果
  • useCallback:memoizes 回调函数
  • useTransition:将 state 更新标记为非紧急
  • useDeferredValue:延迟更新 UI 的非关键部分

Other Hooks

  • useId:为可访问性生成唯一 ID
  • useDebugValue:在 React DevTools 中显示标签

来源

useState 实现

useState Hook 是最常用的 Hook 之一。它提供了组件 state 和用于更新它的函数。

内部流程

来源

状态更新

当调用 useState 的 state setter 时,React 会

  1. 创建一个包含新 state 或更新函数的更新对象
  2. 将其添加到 Hooks 的更新队列中
  3. 调度组件的重新渲染
  4. 在下一次渲染期间,处理所有挂起的更新以计算新 state

来源

useEffect 系统

useEffect Hook 允许函数组件执行副作用。

Effect Phases

React 中的 Effects 在不同的阶段执行

  1. Layout Effects:在所有 DOM 变异之后、浏览器绘制之前同步运行
  2. Passive Effects:在浏览器绘制后异步运行

来源

Effect 生命周期

当使用 useEffect 的组件渲染时

  1. 在渲染期间,捕获 effect 函数和依赖项
  2. 在 commit 期间,React 会将依赖项与之前的依赖项进行比较
  3. 如果依赖项已更改(或这是首次渲染),则调用之前的 effect 的清理函数
  4. 新的 effect 将被安排在浏览器绘制后运行

来源

Hooks 规则

Hooks 系统要求严格遵守特定的规则

  1. 只在顶层调用 Hooks——不在循环、条件或嵌套函数中调用
  2. 只从 React 函数组件或自定义 Hooks 中调用 Hooks——不从常规 JavaScript 函数中调用

这些规则之所以存在,是因为 React 依赖调用顺序将 Hooks 调用与其在渲染之间的状态关联起来。Hooks 实现使用了链表数据结构,并且调用顺序必须保持一致,才能正确地将 Hooks 与其状态关联。

React 提供了 ESLint 规则来自动强制执行这些约束。

来源

自定义 Hooks

自定义 Hooks 是使用 React Hooks 的 JavaScript 函数,它们遵循与内置 Hooks 相同的规则。它们能够提取和重用组件之间的有状态逻辑。

自定义 Hook 模式

自定义 Hooks 遵循与内置 Hooks 相同的执行模型,允许组件重用有状态逻辑,同时为每个组件实例维护独立的状态。

来源

Hooks 调试和 DevTools 集成

React 提供了专门的开发专用功能来调试 Hooks

  1. Hook 警告:检测并警告常见的 Hook 错误(顺序更改、规则违规)
  2. DevTools 集成:在 React DevTools 中显示 Hook 状态和值
  3. useDebugValue:允许自定义 Hooks 在 DevTools 中显示自定义标签

来源

性能考量

Hooks 提供了多种优化来帮助管理组件性能

Memoization Hooks (记忆化 Hooks)

  • useMemo:防止昂贵的计算在每次渲染时运行
  • useCallback:防止回调函数在每次渲染时更改身份

这些 Hooks 有助于打破可能导致子组件不必要重新渲染的“引用相等”链。

渲染优化

  • useTransition:将状态更新标记为非紧急,允许其他更新优先
  • useDeferredValue:创建值的延迟版本,该版本会逐渐过渡

来源

幕后:Hook Dispatcher (Hook 分发器)

React 使用分发器机制,根据当前阶段(渲染、挂载、更新等)提供不同的 Hook 实现。分发器会在组件渲染前被切换,决定将调用哪个 Hook 实现。

来源

与 React Fiber 集成

Hooks 与 React Fiber 架构深度集成

  1. Hook 状态存储在 Fiber 的 memoizedState 属性上
  2. Hook 更新可以通过 Fiber 工作循环调度工作
  3. Effects 通过 Fiber 上的 effect tag 系统进行管理

当 Hook 调度更新时,它会

  1. 创建一个更新对象
  2. 将其添加到组件的更新队列中
  3. 通过 scheduleUpdateOnFiber 调度重渲染
  4. 渲染系统最终会处理这些更新

来源

结论

React Hooks 为管理函数组件中的状态和副作用提供了一个强大的 API。Hooks 系统构建在 React Fiber 架构之上,利用其能力来跟踪组件状态和调度更新。理解 Hooks 的内部结构有助于有效地使用它们并调试可能出现的问题。

Hooks 的规则并非随意限制,而是 Hooks 系统实现方式的直接结果。遵循这些规则可以确保 React 在渲染之间正确地将 Hook 调用与其状态关联起来。