当前位置: 首页 > news >正文

React Hooks底层执行逻辑详解、自定义Hooks、FiberScheduler

React Hooks底层执行逻辑详解

React Hooks 在表面上看像普通的函数调用,背后却隐藏着一套复杂而高效的运行时机制。要理解 React Hooks 的底层执行逻辑,需要从 React 如何管理组件的状态与副作用入手。


🧠 一、React 为什么引入 Hooks?

在 class 组件中,状态逻辑分散、复用困难(通过 HOC、render props 实现)。Hooks 通过“函数组件 + 闭包 + 顺序调用”机制,解决了这些问题,让函数组件拥有“类”的能力。


🔍 二、Hooks 的核心机制:调用顺序与数组栈

✅ 核心思想:

React 内部为每个组件实例维护一个 “Hook 调用栈”(本质上是一个数组),Hooks 的执行顺序严格依赖于你在函数中书写的顺序

举个例子:

function MyComponent() {const [count, setCount] = useState(0)    // 第一个 Hook(索引 0)const [name, setName] = useState('')     // 第二个 Hook(索引 1)
}

React 内部逻辑可以抽象为伪代码:

let hookIndex = 0
const hooks = []function useState(initialValue) {const currentIndex = hookIndexif (isMount) {hooks[currentIndex] = typeof initialValue === 'function' ? initialValue() : initialValue}const setState = (newValue) => {hooks[currentIndex] = newValuetriggerComponentUpdate()}hookIndex++return [hooks[currentIndex], setState]
}

✔️ 所以:每次重新渲染组件时,React 都会以相同顺序重新执行 useXXX(),来匹配状态数组中的值。


🔧 三、Hooks 的执行流程(React 内部机制)

🧩 每次组件渲染时:

  1. 当前组件会进入 render phase

  2. React 初始化 fiberNode.memoizedState(Hook 存储区)

  3. 每次调用一个 Hook(如 useStateuseEffect):

    • React 用当前的 hookIndex 取出对应位置的值
    • 更新完后 hookIndex++
  4. 所有 Hook 调用完毕后:

    • memoizedState 就是这个组件的 Hook 状态链
    • React 将其挂在 fiber 树上,供下一次渲染使用

⚙️ 四、不同 Hook 的底层行为

1️⃣ useState

  • 在首次渲染时保存初始值
  • 后续调用 setState 会触发组件更新,并保留新值在状态数组中

2️⃣ useEffect

  • 注册副作用及清理函数
  • 保存依赖数组,用于下一次渲染对比
  • 在 commit 阶段执行副作用

3️⃣ useRef

  • 保存一个 { current: ... } 对象
  • 是在 Hook 数组中创建的稳定引用,不会重新创建

4️⃣ useMemo / useCallback

  • 保存返回值或函数引用
  • 对比依赖数组决定是否复用旧值

📊 五、Hook 状态存储结构:Fiber Node

每个组件的所有 Hook 状态,都挂载在它自己的 Fiber Node 上的 memoizedState 字段中,它实际上是一个单链表结构

FunctionComponentFiberNode
└── memoizedState --> HookState(useState) └── next --> HookState(useEffect) └── next --> ...

这个结构意味着:

  • 每个 Hook 对应链表中的一个节点
  • 遍历顺序必须与调用顺序保持一致,否则状态将错位(所以 Hooks 不能写在条件语句中)

🚨 六、为什么 Hooks 不能放在 if 语句里

因为状态顺序是通过“调用顺序 + 索引”来维护的。如果你这样写:

if (someCondition) {useState(...) // ⚠️ 不一定会调用
}

那么下一次渲染时,Hook 的数量或顺序可能变化,导致状态错乱(取到了别的 Hook 的状态)!

React 会通过内部开发环境检查这些不规范用法。


🧬 七、Hook 触发组件更新的机制

当调用 setState(newVal)

  1. 会创建一个更新对象,加入到组件的更新队列
  2. 标记当前 Fiber 为“需要更新”
  3. 触发 React Scheduler 安排任务(基于优先级)
  4. 进入 render phase,重新执行组件函数(重新执行所有 Hooks)
  5. 比较 Fiber 树 → 更新 DOM

🧪 八、一个模拟实现:useState

let hookStates = []
let hookIndex = 0function useState(initialValue) {const currentIndex = hookIndexhookStates[currentIndex] = hookStates[currentIndex] || initialValuefunction setState(newVal) {hookStates[currentIndex] = newValrender()}hookIndex++return [hookStates[currentIndex], setState]
}function render() {hookIndex = 0ReactDOM.render(<App />, document.getElementById('root'))
}

✅ 九、总结:Hooks 底层关键点

机制说明
顺序执行原则Hooks 必须按照一致顺序调用
状态数组/链表结构每个 Hook 都在 Fiber 节点的状态链表中占一项
更新触发原理setState 会触发组件的调度和重新渲染
Hooks 本质React 自定义的一套状态和副作用管理系统,依赖“闭包 + 引用 + 顺序”维护状态

自定义Hook

自定义 Hooks 是现代 React 中最重要的模式之一,用于在函数组件之间复用逻辑。它是一种在组件外提取公共逻辑的方式,优雅地替代了以前 class 组件中用 HOC 或 render props 的做法。


🧠 一、自定义 Hook 是什么?

自定义 Hook 就是一个以 use 开头的 JavaScript 函数,内部可以调用其他 Hooks(如 useState、useEffect、useContext 等)

它并不需要拥有特殊的语法,而是遵守命名规范(以 use 开头)和 Hook 规则(只能在顶层和 React 函数组件中调用)


🔧 二、基础语法结构

function useMyHook() {const [state, setState] = useState(initialValue)// 可以包含副作用useEffect(() => {// 例如订阅数据return () => {// 清理操作}}, [])return { state, setState }
}

使用方法:

function MyComponent() {const { state, setState } = useMyHook()return <div>{state}</div>
}

🛠️ 三、常见自定义 Hook 示例

1️⃣ useWindowSize:监听窗口大小

import { useState, useEffect } from 'react'function useWindowSize() {const [size, setSize] = useState({ width: window.innerWidth, height: window.innerHeight })useEffect(() => {const handleResize = () => {setSize({ width: window.innerWidth, height: window.innerHeight })}window.addEventListener('resize', handleResize)return () => window.removeEventListener('resize', handleResize)}, [])return size
}

2️⃣ useFetch:封装数据请求逻辑

import { useState, useEffect } from 'react'function useFetch(url) {const [data, setData] = useState(null)const [loading, setLoading] = useState(true)useEffect(() => {setLoading(true)fetch(url).then(res => res.json()).then(data => {setData(data)setLoading(false)})}, [url])return { data, loading }
}

使用:

const { data, loading } = useFetch('/api/user')

🧩 四、自定义 Hook 的使用场景

场景Hook 示例
状态共享useForm, useTheme, useAuth
业务逻辑抽象usePagination, useLogin
操作 DOMuseScroll, useFocus
事件/副作用封装useOnlineStatus, useDebounce
状态机器或流程控制useWizard, useStepper

🚨 五、自定义 Hook 的注意事项

1. 必须以 use 开头

否则 React 无法检测是否遵守 Hook 的规则。

2. 遵循 Hook 规则(顶层调用)

  • 不要在 if、for、事件中调用 Hook
  • Hook 必须在函数组件或另一个 Hook 中使用
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

Hooks 是按调用顺序管理状态的,调用顺序必须稳定执行上下文必须是“可追踪”的(函数组件或 Hook),否则 React 无法正确管理它们。

3. 避免副作用重复(加依赖)

useEffect(() => {// fetch or something
}, [url]) // 依赖项必须写全

✅ 六、自定义 Hook vs HOC/Render Props

特性自定义 HookHOCRender Props
写法简洁✅ 非常简洁❌ 会嵌套❌ 需要 props 回调
类型推导好✅ 原生支持❌ props 类型麻烦
可组合性✅ 任意组合
推荐程度✅ 推荐使用⛔ 旧方式(不推荐)⛔(不推荐)

📦 七、如何组织自定义 Hook?

建议创建一个 hooks/ 文件夹,每个 Hook 一个文件。例如:

src/
├── hooks/
│   ├── useAuth.js
│   ├── useFetch.js
│   └── useWindowSize.js

✨ 总结

你应该使用自定义 Hook 的时候:
有重复逻辑(如监听、请求、共享状态等)
逻辑独立但不涉及 UI
想增强可维护性和可测试性

如果你告诉我你项目中具体遇到的重复逻辑或问题,我可以帮你定制一个适合你项目的自定义 Hook 模板。是否要尝试?


Fiber&Scheduler

在这里插入图片描述
Fiber 架构 + Scheduler 是 React 实现 并发渲染 的核心。它们分别负责「任务的组织与执行逻辑」和「任务的调度与执行时机」。


🧠 一、React Fiber 架构详解

Fiber 是 React 16 引入的全新架构,用来替代旧的 Stack Reconciler。它的设计目标是:

  • 任务可被拆分(增量渲染)
  • 渲染可中断、恢复、重用
  • 支持并发调度

📦 1. Fiber 是什么?

一个 Fiber 就是一个组件的工作单元(Work Unit),它是一个 JavaScript 对象,描述了:

type FiberNode = {type: Function | string;key: string | null;child: FiberNode | null;sibling: FiberNode | null;return: FiberNode | null; // 父节点stateNode: any; // 组件实例memoizedProps: any;memoizedState: any;alternate: FiberNode | null; // 双缓冲flags: number; // 副作用标记...
}

🧭 2. Fiber Tree 构建过程

每次更新时,React 会创建一棵新的 Fiber 树(work-in-progress tree),由当前树(current tree)复制并修改。

  • 双缓冲机制:current & workInProgress 交替使用
  • 每个更新任务递归生成 Fiber 节点,构成完整 Fiber 树

🔁 3. Fiber 的工作循环(核心阶段)

🔨 Reconciliation(协调阶段)
  • 调用组件函数(或类的 render)生成新的虚拟 DOM
  • 比较新旧 Fiber 树,标记哪些节点需要变更
  • 构建 “Effect List”(副作用链表)
🚀 Commit(提交阶段)
  • 根据 Effect List 执行真实的 DOM 操作(插入/删除/更新)
  • 不可被打断,必须同步完成

🕹️ 二、Scheduler 调度器详解

Scheduler 是 React 的调度核心,负责 管理 Fiber 的执行时机与优先级,使 React 拥有「可中断」和「可恢复」的能力。


📋 1. 核心能力

  • 管理任务队列
  • 计算任务优先级(Lanes)
  • 根据浏览器空闲时间切片执行
  • 决定是否让出主线程(shouldYield

⏳ 2. 时间切片(Time Slicing)

while (work && !shouldYield()) {work = performUnitOfWork(work);
}
  • 每次只做一小部分工作(一个 Fiber 节点)
  • 超出时间阈值就让出执行权,避免卡住主线程

📊 3. 优先级系统(Lanes)

React 引入「Lanes(车道)」作为多优先级调度方案:

const NoLane = 0b00000
const SyncLane = 0b00001       // 同步优先级
const InputContinuousLane = 0b00010 // 用户输入优先
const DefaultLane = 0b00100
const IdleLane = 0b10000       // 最低优先

React 会根据任务类型分配 Lane,调度器根据当前空闲情况,调度最高优先的任务先执行。


⚙️ 三、Fiber + Scheduler 协同流程图

用户触发更新(如点击按钮)↓
Scheduler 收到任务,放入任务队列↓
根据 Lane 决定优先级 & 是否立即执行↓
Fiber Tree 被构建(协调阶段)↓
每个 Fiber 任务以时间切片形式执行↓
中途检查 shouldYield(),必要时中断↓
所有 Fiber 构建完成,进入 commit 阶段↓
一次性提交 DOM 修改(同步执行)

🧩 总结:Fiber & Scheduler 分工

功能FiberScheduler
结构描述每个组件的渲染任务管理任务执行的优先级和时机
拆分任务将渲染工作拆成一个个 Fiber 节点将任务切片处理,防止阻塞主线程
可中断机制通过时间切片,暂停和恢复渲染决定何时中断、恢复和重新调度任务
优先级处理每个 Fiber 带有优先级(lane)任务队列按优先级排序

如果你需要我通过可视化图解、源码级解析(如 Scheduler 源码调度流程),我也可以帮你补充。是否继续?

相关文章:

  • 湖北理元理律师事务所债务优化方案:让还款与生活平衡成为可能
  • 时序数据库 TDengine × Superset:一键构建你的可视化分析系统
  • PyQt学习系列05-图形渲染与OpenGL集成
  • 安全自动化与AI驱动防御
  • 欧拉公式的历史脉络、数学证明和现代意义
  • List转字符串去除[]和空格
  • STM32:深度解析RS-485总线与SP3485芯片
  • LCS4110R加密芯片在打印机墨盒的应用
  • 电子电路:能认为电抗也是在做功吗?
  • microsoft中word如何添加个人签名
  • 【动手学深度学习】2.3. 线性代数
  • GESP编程等级认证C++三级9-字符串2
  • 【SpringBoot】从零开始全面解析Spring IocDI (一)
  • #6 百日计划第六天 java全栈学习
  • uniapp 嵌入鸿蒙原生组件 具体步骤
  • ARFoundation系列讲解 - 77 音频可视化
  • CentOS 7.6 升级 Openssl 及 Openssh 方法文档
  • 达梦数据库-报错-01-[-3205]:全文索引词库加载出错
  • 【图像大模型】AnimateDiff:基于扩散模型的视频生成技术解析与实践指南
  • 机器学习第二十五讲:TensorFlow → 乐高式搭建深度学习模型
  • 河北省做网站哪家公司好/南京网络建站公司
  • 涪城移动网站建设/深圳关键词优化软件
  • 襄阳市建设公司网站/seo营销推广公司
  • 贵阳建设网站/阿里域名注册官网
  • 沈阳网站开发简维/黑龙江头条今日新闻
  • 购物网站用那个软件做/百度指数分析大数据