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 源码调度流程),我也可以帮你补充。是否继续?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/bicheng/82157.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Windows命令实用工具——tcping 命令工具安装及基础使用

Windows命令实用工具——tcping 命令工具安装及使用 一、tcping 命令简介二、tcping 的安装1、tcping 官网下载安装包2、将软件包复制到 Windws 系统的 System32 目录下面3、查看 tcping 命令是否安装成功 三、tcping 工具简单使用方法 一、tcping 命令简介 tcping 的主要功能…

智慧化工园区安全风险管控平台建设方案(Word)

1 项目概况 1.1 园区概况 1.1.1 XX化工园区简况 1.1.2 企业现状 1.1.3 园区发展方向 1.1.4 园区信息化现状 1.2 项目建设背景 1.2.1 政策背景 1.3 项目建设需求分析 1.3.1 政策需求分析 1.3.2 安全生产监管需求分析 1.3.3 应急协同管理需求分析 1.3.4 工业互联网安…

【动手学深度学习】2.3. 线性代数

目录 2.3. 线性代数1&#xff09;标量2&#xff09;向量3&#xff09;矩阵4&#xff09;张量5&#xff09;张量的基本性质6&#xff09;降维7&#xff09;点积8&#xff09;矩阵-向量积9&#xff09;矩阵-矩阵乘法10&#xff09;范数11&#xff09; 小结 2.3. 线性代数 本节将…

如何在项目当中使用redis进行范围搜索

目录 如何将地理位置数据保存到 Redis 中以支持范围查询 Redis 中的 GEO 类型是什么&#xff1f; 如何保存 GEO 数据到 Redis 分段解释&#xff1a; RedisKey.POSTS_ANIMALS_LOCATIONS new Point(longitude, latitude) 如何进行范围搜索 Redis GEO 范围搜索核心语句 1…

物联网低功耗保活协同优化方案:软硬件与WiFi网关动态联动

目录 一、总体方案概述 二、架构组成 2.1 系统拓扑 2.2 硬件端(MCU + WiFi 模组) 2.3 WiFi 网关 2.4 云端服务器 三、低功耗保活技术设计模式 3.1 模式一:定时唤醒 + MQTT 保活 3.1.1 设备端 3.1.2 优势 3.2 模式二:网关保活代理 + 本地网络唤醒 3.2.1 网关功能…

UniApp+Vue3微信小程序二维码生成、转图片、截图保存整页

二维码生成工具使用uqrcode/js&#xff0c;版本4.0.7 官网地址&#xff1a;uQRCode 中文文档&#xff08;不建议看可能会被误导&#xff09; 本项目采用了npm引入方式&#xff0c;也可通过插件市场引入&#xff0c;使用上会略有不同 准备工作&#xff1a; 安装&#xff1a;pnpm…

Zenmap代理情况下无法扫描ip

原因是开了代理会报错 error “only ethernet devices can be used for raw scans on Windows” 在扫描参数后加 -sT -Pn&#xff0c;但会导致结果太多 例如&#xff1a;nmap -sT -T4 -A -v -Pn 10.44.2.0/24 如果你只是想找没人用的IP&#xff0c;你不需要搞复杂的原始层扫描&…

将多个值关联到同一个 key的map(key可以重复的map)示例

在 Java 中&#xff0c;标准的 Map 接口要求 key 必须唯一&#xff0c;如果需要 key 可重复 且保持 插入顺序 的数据结构&#xff0c;可以使用以下方案&#xff1a; 1. 使用 List<Map.Entry<K, V>> 最直接的方式是用链表存储键值对&#xff0c;允许重复 key&…

Arthas(阿尔萨斯)

一、Arthas 是什么&#xff1f; Arthas&#xff08;阿尔萨斯&#xff09;是阿里巴巴开源的一款 Java 在线诊断工具&#xff0c;基于 Java Agent 和字节码增强技术实现。它无需重启 JVM&#xff0c;即可动态追踪代码执行、实时查看 JVM 状态、修改代码逻辑&#xff0c;是生产环…

深入解读Qwen3技术报告(三):深入剖析Qwen3模型架构

重磅推荐专栏&#xff1a; 《大模型AIGC》 《课程大纲》 《知识星球》 本专栏致力于探索和讨论当今最前沿的技术趋势和应用领域&#xff0c;包括但不限于ChatGPT和Stable Diffusion等。我们将深入研究大型模型的开发和应用&#xff0c;以及与之相关的人工智能生成内容&#xff…

UE4游戏查找本地角色数据的方法-SDK

UE4中&#xff0c;玩家的表示通常涉及以下几个类&#xff1a; APlayerController: 代表玩家的控制逻辑&#xff0c;处理输入等。 APawn: 代表玩家在世界中的实体&#xff08;比如一个角色、一辆车&#xff09;。APlayerController 控制一个 APawn。 ACharacter: APawn 的一个…

springboot+vue实现服装商城系统(带用户协同过滤个性化推荐算法)

今天教大家如何设计一个服装商城 , 基于目前主流的技术&#xff1a;前端vue3&#xff0c;后端springboot。 同时还带来的项目的部署教程。 系统最大的亮点是使用了两个推荐算法: 1. 基于Jaccard算法的用户浏览历史推荐。 2. 基于用户的协同过滤算法个性化推荐。 还有核心的商…

ERROR: Could not install packages due to an OSError: [WinError 5] 拒绝访问

有可能是设置了代理 unset ALLPROXY 或者注释掉 当然也有可能是其他原因 权限不足​​ 以管理员身份运行 CMD/PowerShell&#xff0c;或使用 --user 安装 ​​文件被占用​​ 关闭杀毒软件或重启电脑 Python 环境损坏​​ 重新安装 Python 或使用虚拟环境 ​​ 杀毒软件阻止…

【深尚想!爱普特APT32F1023H8S6单片机重构智能电机控制新标杆】

在智能家电与健康器械市场爆发的今天&#xff0c;核心驱动技术正成为产品突围的关键。传统电机控制方案面临集成度低、开发周期长、性能瓶颈三大痛点&#xff0c;而爱普特电子带来的APT32F1023H8S6单片机无感三合一方案&#xff0c;正在掀起一场智能电机控制的技术革命。 爆款基…

一个.NET开源、轻量级的运行耗时统计库

前言 在.NET开发中&#xff0c;为了准确统计对应方法的执行时间&#xff0c;我们最常用的方式是手动使用 Stopwatch 来显式编写计时逻辑&#xff0c;但是假如你需要大量的使用 Stopwatch 来进行耗时统计的话不利于保持代码的整洁和增加代码的维护成本。 项目介绍 MethodTime…

嵌入式鸿蒙openharmony应用开发环境搭建与工程创建实现

各位小伙伴大家好,本周开始分享鸿蒙开发相关的内容,从基础的配置方法到各种功能的实现,探索国产操作系统的奥秘。 第一:观察结果 第二:开源语言 ArkTS是鸿蒙应用开发中使用的TypeScript超集,提供了一套丰富的API来构建应用界面和逻辑。 第三:环境搭建 步骤 1 通过如…

软考 组合设计模式

组合设计模式&#xff08;Composite Pattern&#xff09;是结构型设计模式之一&#xff0c;它的核心思想是将对象组合成树形结构来表示“部分-整体”的层次结构&#xff0c;使得用户对单个对象和组合对象的使用具有一致性。 主要概念&#xff1a; 组件&#xff08;Component&a…

vue 中的v-once

&#x1f530; 基础理解 ✅ 语法&#xff1a; <span v-once>{{ msg }}</span>✅ 效果&#xff1a; • 只渲染一次&#xff0c;之后无论数据如何变化&#xff0c;该内容都不会更新。 • 非常适用于静态内容或首次加载后不需要变化的数据。&#x1f9ea; 示例&…

GPU训练和call方法

知识点回归: CPU性能的查看:看架构代际、核心数、线程数GPU性能的查看:看显存、看级别、看架构代际GPU训练的方法:数据和模型移动到GPU device上类的call方法:为什么定义前向传播时可以直接写作self.fc1(x)import torch import torch.nn as nn import torch.optim as opti…

人脸识别备案开启安全防护模式!紧跟《办法》!

国家互联网信息办公室与公安部于 2025 年 3 月 13 日联合公布了《人脸识别技术应用安全管理办法》&#xff08;以下简称《办法》&#xff09;&#xff0c;并自 2025 年 6 月 1 日起正式施行。其中&#xff0c;人脸识别备案成为了规范技术应用、守护信息安全的关键一环。​ 一、…