React 并发、Fiber 与 Hook 原理指南
引言:从同步堆栈到异步 Fiber 的量子跃迁
在 React 的演进历程中,从版本 15 到 16 的转变并非一次简单的增量更新,而是一场深刻的架构革命。要真正理解 React 并发(Concurrent)模式与 Hooks 的内在机理,必须首先追溯到这次重写的核心——Fiber 架构。它不仅是实现所有现代 React 特性的基石,更代表了一种全新的 UI 渲染哲学。
亟待解决的核心问题:前 Fiber 时代的 React
在 React 16 之前,其核心协调(Reconciliation)算法被称为“堆栈协调器”(Stack Reconciler)1。该算法的运作方式是同步且递归的。当一个组件的状态或属性发生变化时,React 会从组件树的根节点开始,深度优先地递归遍历整个子树,计算出 Virtual DOM 的差异,然后将这些差异一次性应用到真实 DOM 上。
这种模式的根本缺陷在于其“不可中断性”。一旦协调过程开始,它必须一气呵成,直到遍历完所有受影响的节点,清空调用堆栈为止 3。对于结构简单、更新不频繁的应用,这并无大碍。然而,随着应用日益复杂,组件树愈发深邃,这种同步阻塞模型的弊端便暴露无遗:
- 主线程阻塞:对于一个庞大的组件树,一次完整的协调过程可能耗时数百毫秒。在此期间,JavaScript 引擎的主线程被完全占据,无法响应任何其他任务,例如用户的点击、输入或滚动事件,也无法执行动画的下一帧 4。这直接导致了用户界面(UI)的卡顿、掉帧和无响应,严重损害了用户体验 6。
- 无差别对待的更新:堆栈协调器对所有更新一视同仁。无论是源自用户输入的紧急交互,还是后台数据加载这类可稍后处理的更新,都会被置于同等优先级进行处理 5。系统缺乏一种机制来区分任务的轻重缓急,无法优先处理对用户体验至关重要的更新。
Fiber 协调器的诞生:为并发而生的新架构
为了从根本上解决上述问题,React 团队对核心算法进行了彻底的重写,其成果便是 Fiber 协调器(Fiber Reconciler),自 React 16 起成为默认协调器 8。Fiber 并非一个开发者直接使用的功能,而是一个全新的底层架构。它的设计目标明确而宏大:
- 增量渲染(Incremental Rendering):将庞大的渲染任务分解成微小的工作单元,可以分布在多个帧上执行 1。
- 任务优先级(Prioritization):为不同的更新分配不同的优先级,确保高优先级任务(如用户输入)能够抢先执行 10。
- 可中断与可恢复(Pausability and Resumability):赋予 React 在渲染过程中暂停、中止、甚至复用已完成工作的能力 1。
正是 Fiber 架构的这些核心特性,为后续所有高级功能——如并发模式(Concurrent Mode)、Suspense、时间分片(Time-Slicing)以及 Hooks 的高效运作——铺平了道路 6。
本指南的探索路径
本报告旨在为资深工程师构建一个关于现代 React 核心原理的坚实心智模型。我们将遵循一条从底层到上层的逻辑路径,系统性地解构 React 的内部世界。探索将从 Fiber 架构的 foundational 技术开始,逐步深入到它所支持的并发渲染机制,然后剖析 Hooks 如何在这一新架构上实现其优雅而强大的状态管理能力,最终将理论与性能优化实践相结合。
表 1:堆栈协调器 vs. Fiber 协调器:一次范式级的对比
|
属性 |
堆栈协调器 (React < 16) |
Fiber 协调器 (React >= 16) |
|
渲染模型 |
同步、递归、阻塞式 |
异步、迭代、可中断 |
|
核心数据结构 |
系统调用堆栈(隐式) |
Fiber 节点链表(显式在内存中) |
|
工作管理 |
整体式(All-or-nothing) |
颗粒化(工作被拆分为单元/块) |
|
优先级机制 |
无(所有更新同级) |
有(Lanes 模型区分不同优先级) |
|
错误处理 |
不稳定,错误可能污染整棵树 |
通过错误边界(Error Boundaries)改进 |
|
所支持的关键特性 |
基础的 Virtual DOM diffing |
并发渲染、Suspense、时间分片、useTransition |
这张表格清晰地揭示了二者之间的根本差异。它不仅是历史的回顾,更是理解后续所有概念的起点。Fiber 协调器的每一项优势,都直接对应着堆栈协调器的一个痛点,而这些优势的实现细节,正是本报告将要深入剖析的核心。
第一章:解构 Fiber 架构
Fiber 架构是 React 并发模式的基石。理解其设计,关键在于掌握三个核心概念:作为工作单元的 Fiber 节点、作为遍历路径的 Fiber 树(链表结构),以及作为一致性保障的双缓冲技术。
Fiber 节点:工作单元的解剖学
在 Fiber 架构中,一个“fiber”(小写 f)是一个普通的 JavaScript 对象,它不仅仅是组件或 DOM 节点的静态描述,更是一个动态的“工作单元”(Unit of Work)1。在协调过程中,React 应用中的每一个需要处理的元素——无论是类组件、函数组件实例,还是原生的 DOM 节点(如
div),甚至是数组或 Fragment——都会对应一个 fiber 节点 9。
一个 fiber 节点承载了关于一个工作单元的全部信息,其关键属性包括:
- type 和 key:这两个属性直接源自 React Element。type 描述了 fiber 对应的组件类型。对于自定义组件,type 是组件的函数或类本身;对于宿主组件(Host Component),type 是一个字符串,如 'div' 或 'span'。key 则用于在同级节点 diffing 过程中识别节点,判断其是否可以被复用 13。
- stateNode:这是一个指向该 fiber 节点所关联的本地实例的指针。对于类组件,它指向组件的实例;对于宿主组件,它指向真实的 DOM 元素 9。
- pendingProps 和 memoizedProps:pendingProps 是从 React Element 传入的、即将被处理的新 props。memoizedProps 则是上一次渲染完成后“记住”的 props。通过比较 pendingProps 和 memoizedProps,React 可以快速判断 props 是否发生变化,从而决定是否需要跳过该 fiber 的工作,实现性能优化 13。
Fiber 树:伪装成树的链表
尽管我们称之为“Fiber 树”,并且它在逻辑上镜像了组件的层级结构,但其在内存中的物理实现并非传统的树形结构,而是一个基于指针的单向链表集合 1。这一看似微小的实现差异,却是实现渲染中断与恢复的架构关键。
每个 fiber 节点通过以下三个指针来构建这个“链表树”:
- child:指向其第一个子节点的 fiber。
- sibling:指向其下一个兄弟节点的 fiber。
- return:指向其父节点的 fiber。
这种指针结构使得 React 能够以迭代而非递归的方式遍历整个组件树 1。传统的递归遍历依赖于 JavaScript 引擎的调用堆栈(call stack)。调用堆栈是引擎级别的、不透明的结构,一旦进入递归调用链,程序就无法中途暂停,必须执行到底 5。这正是旧版协调器阻塞主线程的根源。
通过将“堆栈帧”的概念用 fiber 节点在用户空间(即 JavaScript 可控的内存)中重新实现,React 获得了对遍历过程的完全控制权 10。React 的调度器可以随时在处理完一个 fiber 节点后暂停工作,记录下当前 fiber 的位置,将控制权交还给浏览器主线程,稍后再从记录的位置精确地恢复执行。这种手动管理“堆栈”的能力,是递归调用无法企及的,也是并发成为可能的根本前提 3。
工作循环:无阻塞的树遍历
基于上述链表结构,React 的工作循环(Work Loop)得以高效、无阻塞地执行。这是一个深度优先的遍历过程,但通过迭代实现:
- 向下(Begin Work):从根 fiber 开始,处理当前 fiber。处理完成后,通过 child 指针移动到它的第一个子节点。
- 向右(Traverse Siblings):如果当前 fiber 没有子节点,或者子节点的工作已完成,则通过 sibling 指针移动到它的兄弟节点。
- 向上(Complete Work):如果当前 fiber 既没有子节点也没有兄弟节点,意味着这个分支的遍历已经完成。此时,通过 return 指针返回到其父节点,并标记父节点的部分工作已完成。
这个过程由 performUnitOfWork 和 beginWork 等内部函数驱动,每次只处理一个 fiber 单元 3。整个工作循环被设计为可以在任何两个 fiber 节点之间暂停。调度器会检查分配给 React 的时间片是否耗尽,如果耗尽,则暂停遍历,等待下一次浏览器空闲时再继续 12。
双缓冲技术:current 树与 workInProgress 树
在异步、可中断的渲染世界里,保证 UI 的一致性至关重要。如果 React 在计算更新的过程中直接修改 DOM,用户可能会看到一个只更新了一半的、破碎的界面。为了解决这个问题,Fiber 采用了图形学中常见的“双缓冲”(Double Buffering)技术 9。
React 在内存中始终维护着两棵 Fiber 树:
- current 树:这棵树代表了当前已经渲染在屏幕上的 UI 状态。它是稳定且不可变的,是用户所见的“真相”15。
- workInProgress 树:当一个更新被触发时(例如 setState 被调用),React 不会直接修改 current 树。相反,它会基于 current 树克隆出一棵全新的 workInProgress 树 15。所有新的计算、协调(diffing)和状态更新都在这棵“草稿树”上进行。
这个过程就像是在后台编辑一份文档的副本。workInProgress 树的构建过程是异步且可中断的。如果在此期间有更高优先级的更新进来,React 可以毫不犹豫地丢弃当前的 workInProgress 树,重新从 current 树开始一个新的克隆和更新过程。
每个 fiber 节点上都有一个 alternate 属性,它是一个指针,将 current 树中的 fiber 与 workInProgress 树中对应的 fiber 连接起来 3。这使得 React 可以在两次渲染之间高效地复用 fiber 节点和其内部状态,而不是每次都从头创建,极大地优化了性能。
只有当整棵 workInProgress 树的协调工作全部完成,并且所有必要的 DOM 更新都计算完毕后,React 才会进入一个被称为“提交(Commit)”的阶段。在这个阶段,它会原子性地将 workInProgress 树切换为新的 current 树,并将所有计算好的 DOM 变更一次性应用到屏幕上 15。
这种将更新的计算过程与更新的应用过程彻底分离的机制,是确保异步渲染环境下 UI 始终保持一致性的核心保障。用户永远不会看到中间状态,只会看到从一个完整的旧状态到另一个完整的新状态的瞬时切换。
第二章:两阶段渲染流程
Fiber 架构将一次完整的更新过程划分为两个泾渭分明的阶段:渲染阶段(Render Phase) 和 提交阶段(Commit Phase)。这种划分并非偶然,而是为了实现“安全中断”这一核心并发目标的精心设计。
阶段一:渲染/协调阶段 (Render/Reconciliation Phase)
这是整个更新流程中最为耗时、也是并发特性得以施展的核心阶段。它的关键特性是异步和可中断17。
在此阶段,React 的主要任务是在内存中构建 workInProgress Fiber 树,并计算出必要的变更。具体工作包括:
- 调用组件函数:从根节点开始,React 遍历 workInProgress 树,对于每个 fiber 节点,它会调用对应的组件函数(或类组件的 render 方法)来获取最新的 React Elements 17。
- 执行 Diffing 算法:React 将组件返回的新 Elements 与 current 树中对应的旧 fiber 节点进行比较(即“协调”或“Diffing”),以确定发生了哪些变化 8。例如,一个组件的 props 是否改变,一个 DOM 节点的属性是否更新,或者一个列表中的元素是否增删或移动。
- 生成副作用列表:此阶段的最终产出不是直接的 DOM 操作。相反,它是在 workInProgress 树上为需要变更的 fiber 节点打上一个“标签”(如 effectTag),用以标记需要执行何种副作用(Side Effect)。这些副作用可能包括 Placement(插入新 DOM 节点)、Update(更新 DOM 节点属性)或 Deletion(删除 DOM 节点)15。最终,这些带有
effectTag 的 fiber 节点会被链接成一个“副作用列表”(effect list)。
渲染阶段最重要的一个特性是,它是一个纯计算过程,不产生任何对用户可见的副作用(如修改 DOM)17。它仅仅是读取当前的 props 和 state,然后返回一个关于 UI 的新描述。这种“纯粹性”使得整个渲染阶段的工作可以被安全地暂停、恢复,甚至在更高优先级的任务到来时被完全丢弃,而不会对应用造成任何破坏性影响 3。如果一个低优先级的渲染任务进行到一半被中断,React 可以简单地抛弃当前的 workInProgress 树,然后为新的高优先级任务重新开始一个新的渲染过程 21。
阶段二:提交阶段 (Commit Phase)
当渲染阶段成功完成,React 就拥有了一棵完整的 workInProgress 树和一个精确描述了所有必要 DOM 操作的副作用列表。此时,流程进入提交阶段。与渲染阶段截然不同,提交阶段是同步且不可中断的 12。
在此阶段,React 会执行以下操作:
- 应用 DOM 变更:React 会遍历渲染阶段生成的副作用列表,并根据每个 fiber 节点的 effectTag,一次性地、同步地执行所有 DOM 的插入、更新和删除操作 9。
- 执行生命周期方法与 Effects:DOM 更新完成后,React 会同步调用相应的生命周期方法,例如类组件的 componentDidMount 和 componentDidUpdate,以及函数组件中 useEffect 的清理函数(cleanup)和新的 effect 函数 15。
- 切换 Fiber 树:最后,workInProgress 树将正式“晋升”为 current 树,成为下一次更新周期的基准 15。
提交阶段的同步性和不可中断性是至关重要的。因为这个阶段直接操作 DOM 并执行可能带有副作用的生命周期代码,如果中途被打断,将导致 UI 状态与 React 内部状态不一致,从而引发难以预测的 bug 16。通过确保提交阶段的原子性,React 保证了从用户视角来看,UI 的每一次更新都是一个完整、一致的事务。
总而言之,这种两阶段的设计是 React 实现并发的核心策略。它将“可中断的计算工作”与“不可中断的提交工作”清晰地分离开来。渲染阶段是 React 施展并发魔法、进行时间分片和优先级调度的舞台,而提交阶段则是确保所有魔法最终能以一个稳定、可预测的方式呈现给用户的坚实保障。
第三章:并发渲染的革命
基于 Fiber 架构和两阶段渲染流程,React 得以实现其最具革命性的特性:并发渲染。这并非指 JavaScript 实现了真正的多线程并行处理,而是通过一系列精巧的调度机制,在单线程环境中模拟出并发的效果,从而极大地提升了应用的响应性。
时间分片:将工作分解为可管理的块
时间分片(Time-Slicing)是并发渲染的核心执行机制。其理念非常直观:与其让一个冗长的渲染任务从头到尾霸占主线程,不如将其切分成许多微小的时间片段来执行 6。
在并发模式下,React 不再是一次性完成整个渲染阶段。取而代之的是,它会工作一小段时间(通常是大约 5 毫秒),然后主动将主线程的控制权交还给浏览器 22。这种“让步”(yielding)的行为是关键所在。它为浏览器提供了一个宝贵的窗口期,去处理队列中可能存在的更高优先级的任务,比如响应用户的点击事件、处理键盘输入,或是渲染动画的下一帧 9。
这个概念借鉴了操作系统中的“协作式多任务处理”(Cooperative Scheduling)25。在协作式模型中,一个正在运行的任务需要主动放弃 CPU 控制权,以便其他任务能够运行。React 的调度器正是扮演了这个协作的角色,确保自身的渲染工作不会“饿死”其他重要的浏览器任务 27。
协作式调度:与浏览器事件循环共舞
React 的调度器(Scheduler)通过与浏览器事件循环(Event Loop)的紧密协作来实现时间分片和让步 24。
为了在让出主线程后能够尽快恢复工作,调度器使用了 MessageChannel API(在可用时,比 setTimeout(0) 更优)来安排下一个工作单元在事件循环的下一个宏任务(macrotask)中执行 21。这相当于将任务放入事件队列的末尾,从而有效地将控制权交还给浏览器,让浏览器有机会处理微任务(microtasks)和渲染更新。
在每个工作循环中,调度器都会通过一个类似 shouldYield 的内部检查来判断是否应该暂停 24。这个判断基于自开始工作以来所经过的时间。如果时间超出预算,它就会停止处理新的 fiber 节点,并安排下一次的继续执行。
优先级调度:Lanes 模型
仅仅实现中断和恢复是不够的,一个智能的调度系统还需要知道何时应该中断,以及接下来应该执行哪个任务。在 React 中,并非所有更新都具有同等的紧迫性。例如,用户输入框的回显必须是即时的,而一个复杂图表的数据更新则可以稍缓。为了处理这种差异,React 引入了一套复杂的优先级系统,其内部实现被称为“Lanes 模型”5。
Lanes 模型是一种高效的优先级管理机制,其核心思想是:
- 用位(Bit)代表优先级:模型中没有使用传统的数字或字符串来表示优先级,而是将 31 位的二进制数中的每一位(bit)作为一个“车道”(Lane)。不同的车道代表不同的优先级。例如,最右边的位可能代表最高的优先级 5。
- 用位掩码(Bitmask)管理优先级集合:一个 32 位整数(位掩码)可以同时表示多个待处理的优先级。这种数据结构极其高效,因为检查某个优先级是否存在、合并多个优先级等操作,都可以通过速度极快的位运算(如 & 和 |)来完成。这远比操作数组或对象等数据结构要快得多。
- 为更新分配 Lane:不同类型的更新会被分配到不同的 Lane。例如:
- 源自点击、输入等离散用户事件的更新,会被分配到最高优先级的 SyncLane,要求同步执行 29。
- 被 startTransition 包裹的更新,会被分配到较低优先级的 TransitionLane 7。
- 还有用于处理后台任务的 IdleLane 等。
当 React 调度器决定下一个工作单元时,它会检查应用根节点上挂起的 Lanes 位掩码,并选择优先级最高的 Lane(即最靠右的那个为 1 的位)所代表的任务来执行 5。如果在一个低优先级的
TransitionLane 任务正在执行时,一个高优先级的 SyncLane 更新被触发,调度器会立即中断当前工作,转而处理 SyncLane 的更新。处理完毕后,再决定是恢复还是放弃之前的低优先级工作 22。
Lanes 模型本质上是一个用位运算实现的、高度优化的优先级队列。它的设计充分体现了 React 对核心调度循环性能的极致追求,确保了优先级判断和任务切换的开销降到最低。
通过时间分片、协作式调度和 Lanes 模型的协同工作,React 的并发渲染得以实现。它并非真正的并行计算,而是一种在单线程上通过智能调度实现的“伪并发”,其最终目标是在保证复杂计算能够进行的同时,始终维持 UI 对用户操作的即时响应。
第四章:掌握并发 API
React 并发模式的强大之处在于,它不仅在内部实现了复杂的调度逻辑,还通过一组简洁的 API 将调度控制权部分地开放给了开发者。startTransition 和 useDeferredValue 是这套 API 的核心,它们是开发者用来引导 React 调度器、优化用户体验的主要工具。
startTransition 与 useTransition
startTransition 是一个函数,它允许开发者将一个或多个状态更新标记为“非紧急”的“过渡”(Transition)21。这相当于向 React 调度器发出了一个明确的信号:“这个更新可能会导致开销较大的 UI 变化,请在不阻塞用户交互的前提下,在后台进行渲染。”3。
当一个状态更新被 startTransition 包裹后,React 会将其分配到一个较低优先级的 TransitionLane,而不是默认的高优先级 SyncLane 33。这意味着,如果此时有更高优先级的更新(如用户输入)发生,React 会中断这个过渡更新的渲染过程,优先处理紧急任务,待主线程空闲后再回来继续或重新开始过渡更新 21。
useTransition 是一个 Hook,它提供了与 startTransition 相同的功能,但额外返回一个布尔值 isPending 34。这个
isPending 状态在过渡开始时变为 true,在过渡渲染完成并提交到屏幕后变为 false。这对于向用户提供视觉反馈(如显示加载指示器或禁用按钮)至关重要,让用户知道后台正在发生一些事情 35。
典型用例:响应式搜索框
一个经典的用例是实现一个带有实时结果列表的搜索框。用户的输入操作应该立即反映在输入框中,这是一个高优先级的任务。而根据输入内容过滤一个庞大的数据列表并重新渲染,则是一个可能耗时较长的低优先级任务。
JavaScript
import { useState, useTransition } from'react';
functionSearchPage() {
const = useTransition();
const [inputValue, setInputValue] = useState('');
const = useState('');
const handleChange = (e) => {
// 1. 紧急更新:立即更新输入框的显示内容
setInputValue(e.target.value);
// 2. 过渡更新:将可能耗时的列表过滤标记为非紧急
startTransition(() => {
setSearchQuery(e.target.value);
});
};
return (
<div>
<inputtype="text"value={inputValue}onChange={handleChange} />
{isPending && <div>Loading results...</div>}
<SearchResultsquery={searchQuery} />
</div>
);
}
在这个例子中,setInputValue 是一个常规的状态更新,React 会同步、高优先级地处理它,确保用户的输入能够立即显示。而 setSearchQuery 被包裹在 startTransition 中,React 会在后台渲染 SearchResults 组件。即使用户输入速度很快,输入框也不会卡顿,因为列表的渲染不会阻塞输入框的更新 7。
useDeferredValue
useDeferredValue 是另一个强大的并发 Hook,它提供了与 useTransition 类似的效果,但作用于值而不是状态更新函数38。
这个 Hook 接收一个值,并返回该值的一个“延迟”版本。这个延迟版本的值会“滞后于”最新的值。当组件因为紧急更新而重新渲染时,useDeferredValue 会继续返回旧的值;与此同时,React 会在后台用新的值启动一个低优先级的重新渲染。只有当后台渲染完成后,这个 Hook 才会返回新的值 22。
何时使用 useDeferredValue?
当开发者无法直接控制触发更新的 setState 函数时,useDeferredValue 就显得尤为有用。这种情况通常发生在:
- 需要延迟的值来自父组件的 props。
- 值由第三方库或自定义 Hook 管理,没有暴露其状态更新函数。
JavaScript
import { useState, useDeferredValue } from'react';
functionSearchResults({ query }) {
// query 来自 props,我们无法用 startTransition 包裹它的 set 函数
const deferredQuery = useDeferredValue(query);
// 使用 deferredQuery 进行耗时的计算或渲染
const filteredList = filterLargeList(deferredQuery);
// 可以通过比较 query 和 deferredQuery 来显示“正在加载”的状态
const isStale = query!== deferredQuery;
return (
<div>
{isStale && <div>Updating list...</div>}
<Listitems={filteredList} />
</div>
);
}
在这个例子中,SearchResults 组件接收一个 query prop。我们无法修改父组件调用 setQuery 的方式。通过 useDeferredValue(query),我们创建了一个 deferredQuery。当父组件的 query 快速变化时(例如用户在输入),SearchResults 会先用旧的 deferredQuery 快速重新渲染(几乎无开销),保持界面的响应性。同时,React 会在后台用新的 query 值来计算新的 deferredQuery 和 filteredList,完成后再更新 UI 39。
useTransition vs. useDeferredValue
可以认为这两个 Hook 是同一并发问题的两种不同解决方案,它们的区别在于干预数据流的位置 37:
- useTransition:作用于因(状态更新的源头)。当你有权访问并可以修改 setState 调用时使用。
- useDeferredValue:作用于果(已经变化了的值)。当你只能接收变化后的值,而无法控制其更新过程时使用。
从本质上讲,这些并发 API 是开发者与 React 内部 Lanes 调度模型进行交互的桥梁。它们提供了一种声明式的方式,来告知 React 哪些 UI 更新可以被延迟和中断,从而将复杂的性能调度问题转化为简单、可控的 API 调用。
第五章:Hooks 的起源:从类组件到范式转移
在深入探讨 Hooks 的内部工作原理之前,理解它们为何被创造出来至关重要。Hooks 的出现并非仅仅为了提供一种新的语法糖,而是为了从根本上解决类组件(Class Components)在长期实践中暴露出的三大核心问题 40。
类组件的困境
在 Hooks 诞生之前,类组件是 React 中实现状态管理和生命周期逻辑的唯一方式。然而,随着应用复杂度的提升,开发者们发现自己正面临以下挑战:
- 逻辑复用的“包装地狱”(Wrapper Hell):在类组件中,复用有状态逻辑(Stateful Logic)的主要模式是高阶组件(Higher-Order Components, HOCs)和渲染属性(Render Props)。虽然这些模式功能强大,但它们都存在一个共同的副作用:引入额外的组件层级。一个应用了多个 HOC 的组件,在 React 开发者工具中会呈现出深层嵌套的结构,这使得调试和理解数据流变得异常困难,也就是所谓的“包装地狱”41。
- 混乱且分散的生命周期方法:类组件的生命周期方法(如 componentDidMount、componentDidUpdate、componentWillUnmount)迫使开发者将相关的逻辑拆分到不同的方法中。例如,一个订阅外部数据源的逻辑,其订阅代码通常放在 componentDidMount,处理更新的代码放在 componentDidUpdate,而取消订阅的清理代码则放在 componentWillUnmount。这种按“生命周期”而非“功能”组织代码的方式,使得单个功能的逻辑散落在组件的各个角落,难以维护和追踪,也容易因忘记清理而导致内存泄漏等 bug 44。
- 令人困惑的 this 关键字和类语法:JavaScript 中的 this 关键字本身就是一个学习难点。在类组件中,事件处理函数需要手动绑定 this,否则在调用时会丢失上下文,这是初学者乃至有经验的开发者都经常遇到的问题。此外,类组件的语法对于习惯了函数式编程范式的开发者来说,显得较为笨重和冗长 45。
Hooks 的设计哲学
Hooks 的设计旨在通过一套全新的原语来解决上述所有问题,让开发者能够从函数组件中“钩入”(hook into)React 的状态和生命周期特性 43。其核心设计哲学体现在以下两点:
- 按功能组织,而非生命周期:Hooks 允许开发者将组件中相互关联的部分(例如,设置订阅和清理订阅)封装在一起。useEffect Hook 就是这一理念的最佳体现。所有与某个特定副作用相关的逻辑——包括设置、更新和清理——都可以被放在同一个 useEffect 内部,实现了逻辑上的高内聚 41。这使得组件代码更加清晰、易于理解。
- 简化状态逻辑复用:Hooks 提供了一种极其简单的方式来提取和复用有状态逻辑,那就是创建自定义 Hook。自定义 Hook 本质上只是一个以 use 开头的普通 JavaScript 函数,它可以调用其他 Hooks(如 useState、useEffect)。通过将相关逻辑封装到自定义 Hook 中,任何组件都可以通过一次函数调用来使用这段逻辑,而无需引入任何额外的组件嵌套。这彻底解决了 HOC 和 Render Props 模式带来的“包装地狱”问题,使得组件树保持扁平 41。
从更深层次来看,Hooks 的引入代表了 React 组件模型从**继承(Inheritance)向组合(Composition)**的彻底转变。类组件在形式上依赖于 extends React.Component 的继承模型。而 Hooks 则将组合的思想贯彻到了组件的内部。一个函数组件可以被看作是由多个基础能力(通过调用不同的 Hooks)组合而成的。例如,一个组件可以组合 useState 的状态管理能力、useEffect 的副作用处理能力以及 useCustomLogic 的自定义业务逻辑能力。
这种内部组合的方式,比 HOCs 和 Render Props 在组件外部进行包装的组合方式更为直接和灵活。它完美契合了 React 一贯倡导的“通过组合小型、独立的单元来构建复杂 UI”的核心思想,并将这一思想从组件之间延伸到了组件之内 41。因此,Hooks 并非对类组件的简单替代,而是一次深刻的范式转移,旨在提供一种更符合 React 哲学、更具表现力和更易于维护的组件构建方式。
第六章:深入底层:useState 与 useEffect 的运行机制
Hooks 的 API 表面上看起来简洁明了,甚至有些“神奇”。然而,其背后是一套基于 Fiber 架构和 JavaScript 闭包的精巧实现。理解这套机制,是彻底揭开 Hooks 神秘面纱的关键,也是解释“Hooks 规则”为何存在的根本原因。
Hook 的数据结构:Fiber 节点上的链表
一个普遍的误解是,React 可能在某个全局对象中存储所有组件的 Hook 状态。事实并非如此。每个函数组件实例的所有 Hook 状态,都以一个单向链表的形式,存储在该组件对应 Fiber 节点的 memoizedState 属性上48。
这个链表中的每一个节点,都代表了一次 Hook 的调用(如一次 useState 或 useEffect)。每个节点至少包含两个关键信息:
- memoizedState:存储着这个 Hook 当前的状态值。对于 useState,它就是状态变量本身;对于 useEffect,它存储的是一个包含 effect 函数、依赖项数组和清理函数的对象。
- next:一个指向链表中下一个 Hook 节点的指针 48。
当一个组件首次渲染时,React 会按照 Hooks 的调用顺序,逐一创建这些 Hook 节点,并将它们链接起来,形成一个完整的链表挂载到 Fiber 节点的 memoizedState 上。
揭秘 Hooks 规则的根源
正是上述的链表存储结构,直接导致了著名的“Hooks 规则”的产生,尤其是第一条规则:只能在函数组件的顶层调用 Hooks,不要在循环、条件或嵌套函数中调用 Hook。
这条规则的背后逻辑是:React 完全依赖于每次渲染时 Hooks 的调用顺序来正确地从链表中获取对应的状态46。
- 工作原理:在组件的每次渲染过程中,React 内部会维护一个当前 Hook 的索引(hookIndex),初始为 0 50。
- 当第一个 Hook(比如 useState)被调用时,React 会取出链表中索引为 0 的节点,并返回其 memoizedState。然后 hookIndex 递增为 1。
- 当第二个 Hook 被调用时,React 会取出索引为 1 的节点,返回其状态,然后 hookIndex 递增为 2。
- 这个过程依次进行,直到组件渲染结束。
- 为何不能有条件调用:假设一个 Hook 被放在一个 if 语句中。在某次渲染中,条件为 true,该 Hook 被执行,链表和调用顺序都正常。但在下一次渲染中,如果条件变为 false,该 Hook 被跳过。此时,后续所有 Hooks 的调用顺序都将前移一位。当 React 试图按顺序访问链表时,就会发生错位:它会把本应属于第三个 Hook 的状态节点,错误地分配给第二个 Hook,从而导致严重的状态混乱和 bug 48。
因此,保证 Hooks 在每次渲染中都以完全相同的顺序被调用,是这套机制能够正确工作的绝对前提。
useState 与 useEffect 的分步解析
useState 的工作流程
- 首次渲染 (Mount):当组件首次渲染并调用 useState(initialValue) 时,React 内部的 mountState 函数会被执行。它会创建一个新的 Hook 节点,将 initialValue 存入节点的 memoizedState,然后将这个新节点追加到当前组件 Fiber 的 Hook 链表末尾 50。它返回一个数组,包含
initialValue 和一个 state setter 函数(如 setCount)。 - 更新渲染 (Update):当组件因为其他原因重新渲染时,再次调用到同一个 useState。此时,React 内部的 updateState 函数会被执行。它会根据当前的 hookIndex 找到链表中对应的 Hook 节点,直接读取并返回该节点的 memoizedState 51。
- 状态更新:useState 返回的 setter 函数(如 setCount)是一个与特定 Fiber 节点和特定 Hook 节点绑定的函数。当你调用 setCount(newValue) 时,它并不会立即改变当前的状态值。相反,它会在该组件的 Fiber 节点上创建一个“更新”对象,并将其放入一个更新队列中,然后调度一次新的渲染 52。在下一次渲染该组件时,React 会处理这个更新队列,计算出新的状态值,存入 Hook 节点的
memoizedState,并返回给组件。
useEffect 的工作流程
useEffect 的执行与组件的渲染和提交两个阶段紧密相关:
- 渲染阶段:当组件渲染并调用 useEffect(effectFn, deps) 时,React 同样会找到 Hook 链表中对应的节点。它会将新的 effectFn 和 deps(依赖项数组)存入节点。然后,它会比较新的 deps 和上一次渲染时存储在节点中的旧 deps。如果 deps 没有提供(undefined),或者 deps 数组中的任何一项发生了变化(使用 Object.is 比较),React 就会为这个 effect 打上一个需要执行的标记 53。
重要的是,effectFn 本身在渲染阶段并不会被执行。 - 提交阶段:在 React 完成渲染阶段,并将所有 DOM 变更提交到屏幕之后,它会异步地开始执行所有被标记了需要运行的 effects 54。
- 清理 (Cleanup):在执行新的 effect 之前,如果上一次的 effect 返回了一个清理函数,React 会先执行这个清理函数 54。这确保了旧的订阅或事件监听器等副作用被妥善移除。
- 执行 (Execution):然后,React 执行新的 effectFn。如果这个新的 effectFn 返回了一个函数,这个函数将被保存起来,作为下一次需要执行的清理函数。
核心机制的融合:Fiber 与闭包
Hooks 的实现是 React 两大核心概念的完美结合:Fiber 架构和 JavaScript 闭包。
- Fiber 作为持久化存储:Fiber 节点为函数组件提供了一个在多次渲染之间保持不变的“地方”。函数组件本身在每次渲染时都会重新执行,其内部的局部变量会随之销毁。而 Fiber 节点及其上的 Hook 链表则像是一个与组件实例永久关联的“记忆单元”,持久化地存储着状态 1。
- 闭包作为连接桥梁:JavaScript 闭包是连接函数组件的瞬时执行上下文与 Fiber 节点的持久化状态之间的桥梁。
- useState 返回的 setter 函数就是一个闭包。它“记住”了它所属的组件 Fiber 和它在 Hook 链表中的位置。因此,无论你在何处(如事件处理函数中)调用它,它都能准确地知道要为哪个组件的哪个状态发起更新 56。
- useEffect 接收的 effectFn 也是一个闭包。它“捕获”了其被创建的那一次渲染作用域中的 props 和 state。这就是为什么在 effect 内部访问到的 state 值总是与那次渲染相对应的值,形成了一个“快照”(snapshot)54。
综上所述,Hooks 并非魔法。它们是一套设计精良的系统,其中 Fiber 架构提供了状态持久化的物理基础,而闭包则提供了从瞬态的函数执行作用域安全地访问和操作这些持久化状态的机制。
第七章:高级模式与性能策略
对 React 底层原理的深入理解,最终要服务于构建更高效、更健壮的应用程序。本章将前述关于 Fiber、并发和 Hooks 的知识融会贯通,探讨如何在实践中运用这些原理进行高级性能优化和调试。
综合运用:内部原理如何指导优化实践
对 React 内部工作流的清晰认知,能够让我们在性能优化时做出更明智的决策:
- 理解渲染成本:知道每一次状态更新都可能触发函数组件的整体重新执行,这让我们更加重视 memoization 的必要性。每一次函数调用都意味着新的变量声明、函数创建和 JSX 对象生成,这些累积起来就是渲染成本。
- 区分 Effect 时机:理解 useEffect 在提交阶段之后异步执行,而 useLayoutEffect 在提交阶段之中、浏览器绘制之前同步执行,这一点至关重要。对于需要读取或修改 DOM 布局并希望用户看不到中间闪烁的视觉副作用(如测量元素尺寸并设置其位置),必须使用 useLayoutEffect 55。而对于绝大多数副作用(如数据获取、设置订阅),使用
useEffect 可以避免阻塞浏览器绘制,从而提升应用的响应感。
并发世界中的 Memoization
React.memo、useMemo 和 useCallback 依然是防止不必要重渲染的核心工具,但在并发模式下,它们的重要性被进一步放大了 59。
在并发渲染中,一个正在进行的低优先级渲染任务可能会被高优先级任务中断,甚至其渲染结果最终被丢弃。如果这个被丢弃的渲染任务中包含了大量未经优化的昂贵计算,那么这些计算所消耗的 CPU 时间就完全被浪费了。
- useMemo:用于缓存昂贵的计算结果。在并发场景下,它可以确保即使一个渲染任务被中断并重新开始,只要依赖项没有改变,昂贵的计算就不需要重复执行 47。
- useCallback:用于缓存函数实例。这对于传递给经过 React.memo 优化的子组件的事件处理函数尤为重要。它可以防止因为父组件重渲染导致函数实例变化,从而避免了对子组件的不必要重渲染 62。
- React.memo:对组件进行浅层 props 比较,是阻断渲染从父组件向下传播的关键屏障。
在并发时代,Memoization 不仅是减少单次渲染时间的工具,更是避免在可中断、可丢弃的渲染任务中浪费计算资源的关键策略。
使用 React 开发者工具调试并发行为
React 开发者工具中的 Profiler 是调试并发性能问题的利器 63。
- 火焰图(Flamegraph)与排行榜(Ranked Chart):Profiler 记录的每一次提交(commit)都会生成火焰图,直观地展示了每个组件的渲染耗时。这可以帮助快速定位性能瓶颈组件 65。
- “Record why each component rendered”:在 Profiler 的设置中启用此选项,是诊断不必要重渲染的“法宝”。当选中某次提交和某个组件时,它会明确指出该组件重渲染的原因,是“Props changed”、“Hooks changed”还是“Parent component rendered”65。
- 时间线视图(Timeline):虽然 Profiler 主要关注已提交的渲染,但结合 Chrome Performance 面板,可以观察到 React 调度器的工作情况,识别出长时间运行的任务(Long Tasks),这些正是需要使用 startTransition 来优化的潜在目标 23。当 Profiler 显示某个组件的 Hooks 发生变化但无法确定是哪个 Hook 时,可以在开发者工具的 “Components” 标签页中检查该组件,查看每个 Hook 的当前值,通过前后对比来定位问题。
并发模式下的常见陷阱
转向并发模式需要开发者调整一些过去的编码习惯,并注意以下常见陷阱:
- 对自动批处理的错误依赖:在 React 18 中,所有的状态更新,无论它们发生在何处(事件处理函数、Promise、setTimeout),都会被自动批处理(Automatic Batching)67。这意味着在同一个事件循环 tick 中的多个
setState 调用只会被合并为一次重渲染。过去一些依赖于每次 setState 后立即同步重渲染的代码逻辑可能会因此失效。 - 状态撕裂(Tearing):这是一个在并发渲染中可能出现的微妙问题。当一个外部数据源(如 Redux store)在一次可中断的渲染过程中发生了变化,可能导致 UI 的不同部分在同一时刻渲染出基于不同版本的数据,造成视觉上的不一致。这个问题主要影响外部状态管理库的集成。为了解决它,React 18 引入了 useSyncExternalStore Hook,为外部 store 提供了与 React 并发渲染安全同步的标准方式 21。
- 滥用 startTransition:startTransition 是一个强大的工具,但不应被滥用。将每一个状态更新都包裹在 startTransition 中是一个反模式。它应该被审慎地用于那些确实会导致显著渲染延迟且非关键的更新上。对于需要即时反馈的交互,常规的状态更新仍然是正确的选择 59。
总而言之,现代 React 的性能优化策略已经从单一的**“减少渲染”维度,扩展到了“减少渲染”与“调度渲染”**并重的双重维度。一个优秀的 React 工程师不仅要懂得如何通过 memoization 消除不必要的工作,还要懂得如何利用并发 API 来合理安排那些必要但耗时的工作的优先级,确保关键的用户交互路径始终保持流畅。
结论:构建现代 React 的心智模型
从堆栈协调器的阻塞式递归,到 Fiber 架构的异步迭代;从无差别的更新处理,到基于 Lanes 模型的精细化优先级调度;从类组件的生命周期纠葛,到 Hooks 的功能化组合。React 的演进是一条清晰的逻辑链,其每一步都建立在前一步的基础之上,共同构筑了当今我们所见的这个强大而灵活的 UI 库。
对于资深工程师而言,掌握现代 React 的关键在于构建一个全新的心智模型,其核心原则如下:
- Fiber 是基础:所有并发特性和 Hooks 的实现都根植于 Fiber 架构。其作为工作单元的节点设计、作为遍历机制的链表结构、以及作为一致性保障的双缓冲机制,共同实现了渲染过程的可中断性。
- 渲染是可中断的计算:将渲染视为一个分为两阶段的过程。可中断、异步的渲染阶段负责纯粹的计算工作,而不可中断、同步的提交阶段负责将计算结果原子性地应用到屏幕上。
- 调度是核心:并发模式的本质是协作式调度。React 通过时间分片和优先级(Lanes)管理,在单线程环境中智能地安排工作,确保高优先级任务(如用户输入)能够抢占低优先级任务(如数据更新),从而保持 UI 的响应性。
- Hooks 是状态与 Fiber 的连接器:Hooks 并非魔法,而是利用 Fiber 节点作为持久化存储,通过 JavaScript 闭包将函数组件的瞬时作用域与这份持久化状态安全地连接起来。其调用顺序的稳定性是这套机制正常工作的基石。
- 并发 API 是开发者的指挥棒:useTransition 和 useDeferredValue 等 API,是开发者向 React 内部调度器传达意图的工具,用以声明式地将某些更新标记为非紧急,从而参与到并发调度中。
新的心智模型
应当摒弃将 React 渲染视为单一、同步事件的旧观念。取而代之的新模型是:UI 的更新是一个持续的、被智能调度的、与浏览器协作的工作流。 在这个模型中,React 不再是一个埋头苦干的执行者,而是一个懂得轻重缓急、能够审时度势的协调者。它总是在用户体验和计算效率之间寻求最佳平衡,确保应用即使在处理复杂任务时,也能对用户的操作给予即时反馈。
对资深工程师的最终建议
- 拥抱并发思维:在设计组件和数据流时,主动思考哪些更新是紧急的,哪些是可以推迟的。利用并发 API 将这种思考转化为代码,从架构层面提升应用的响应能力。
- 精准调试:熟练运用 React 开发者工具的 Profiler。当遇到性能问题时,首先要诊断的是“不必要的渲染”还是“必要的但缓慢的渲染”。前者用 memoization 解决,后者用并发 API 解决。
- 深入源码(适时):虽然日常开发无需深入源码,但当遇到棘手的并发问题或需要实现高度优化的自定义 Hook 时,对 Fiber 节点结构、更新队列和调度器工作原理的了解,将提供解决问题的根本性思路。
通过将这些底层原理内化为直觉,资深工程师将能够超越 API 的表层使用,真正驾驭 React 的全部潜力,从而设计出不仅功能强大,而且性能卓越、用户体验一流的现代 Web 应用程序。
Works cited
- An Introduction to React Fiber - The Algorithm Behind React - Velotio Technologies, accessed on August 16, 2025, https://www.velotio.com/engineering-blog/react-fiber-algorithm
- React Reconciliation: From Stack to Fiber — What Changed and Why It Matters, accessed on August 16, 2025, https://dev.to/darshanakkhichi/react-reconciliation-from-stack-to-fiber-what-changed-and-why-it-matters-23gp
- A deep dive into React Fiber - LogRocket Blog, accessed on August 16, 2025, https://blog.logrocket.com/deep-dive-react-fiber/
- Unraveling the Threads: A Deep Dive into React Fiber | by NKpeswani | Medium, accessed on August 16, 2025, https://medium.com/@nikkipeswani94/unraveling-the-threads-a-deep-dive-into-react-fiber-60f8d187d831
- React Fiber: A Tale of Two Reconcilers | by Sen Lin | The Tech Collective - Medium, accessed on August 16, 2025, https://medium.com/the-tech-collective/react-fiber-a-tale-of-two-reconcilers-8e1d2cfeb2b2
- Understanding React Fiber: How It Improves Rendering Performance - OpenReplay Blog, accessed on August 16, 2025, https://blog.openreplay.com/understanding-react-fiber-improves-rendering-performance/
- Keeping React Fast: A Deep Dive into Concurrent Mode, Transitions, and Lanes - Medium, accessed on August 16, 2025, https://medium.com/@divyanshjn23/keeping-react-fast-a-deep-dive-into-concurrent-mode-transitions-and-lanes-467a1f562130
- Understanding React's Fiber Tree: A Deep Dive into React's Architecture and Rendering Process - DEV Community, accessed on August 16, 2025, https://dev.to/gervaisamoah/understanding-reacts-fiber-tree-a-deep-dive-into-reacts-architecture-and-rendering-process-2loo
- Introduction to React Fiber - DEV Community, accessed on August 16, 2025, https://dev.to/jehnz/introduction-to-react-fiber-48c4
- What Do You Know About the New Fiber Structure of React.js? - Habilelabs, accessed on August 16, 2025, https://www.habilelabs.io/blog/what-do-you-know-about-the-new-fiber-structure-of-react-js
- Understanding React Fiber: A Deep Dive into React's Rendering Engine - Medium, accessed on August 16, 2025, https://medium.com/@neelendra1destiny/understanding-react-fiber-a-deep-dive-into-reacts-rendering-engine-c05ff1babb88
- React Fiber Architecture - An Overview, accessed on August 16, 2025, https://tusharf5.com/posts/react-fiber-overview/
- acdlite/react-fiber-architecture - GitHub, accessed on August 16, 2025, https://github.com/acdlite/react-fiber-architecture
- How and why on React's usage of linked list in Fiber to walk the component's tree, accessed on August 16, 2025, https://angular.love/the-how-and-why-on-reacts-usage-of-linked-list-in-fiber-to-walk-the-components-tree/
- An in-depth overview of the React fiber reconciliation algorithm - AG Grid Blog, accessed on August 16, 2025, https://blog.ag-grid.com/inside-fiber-an-in-depth-overview-of-the-new-reconciliation-algorithm-in-react/
- React Fiber Reconciler - Medium, accessed on August 16, 2025, https://medium.com/@akashsdas_dev/react-fiber-reconciler-11986b384324
- How rendering works: The commit phase - react - DEV Community, accessed on August 16, 2025, https://dev.to/chi_code/how-rendering-works-the-commit-phase-29l
- React Fiber Explained: Revolutionizing Performance and User Experience - CodeParrot, accessed on August 16, 2025, https://codeparrot.ai/blogs/react-fiber-explained-revolutionizing-performance-and-user-experience
- Reconciliation - React, accessed on August 16, 2025, https://legacy.reactjs.org/docs/reconciliation.html
- Render and Commit - React, accessed on August 16, 2025, https://react.dev/learn/render-and-commit
- React v18.0, accessed on August 16, 2025, https://react.dev/blog/2022/03/29/react-v18
- Concurrent Rendering - ui.dev, accessed on August 16, 2025, https://ui.dev/c/react/concurrent-rendering
- How React 18 Improves Application Performance - Vercel, accessed on August 16, 2025, https://vercel.com/blog/how-react-18-improves-application-performance
- Scheduling in React - spiess.dev, accessed on August 16, 2025, https://spiess.dev/blog/scheduling-in-react
- Time Slicing in CPU scheduling - GeeksforGeeks, accessed on August 16, 2025, https://www.geeksforgeeks.org/computer-organization-architecture/time-slicing-in-cpu-scheduling/
- Cooperative Scheduling vs Preemptive Scheduling? - Stack Overflow, accessed on August 16, 2025, https://stackoverflow.com/questions/46015648/cooperative-scheduling-vs-preemptive-scheduling
- Process Scheduling in Operating Systems - Tutorialspoint, accessed on August 16, 2025, https://www.tutorialspoint.com/operating_system/os_process_scheduling_qa2.htm
- How React Scheduler works internally? - JSer.dev, accessed on August 16, 2025, https://jser.dev/react/2022/03/16/how-react-scheduler-works/
- How does React re-render internally? - JSer.dev, accessed on August 16, 2025, https://jser.dev/2023-07-18-how-react-rerenders/
- React Concurrent Mode: Optimizing React Performance - DEV Community, accessed on August 16, 2025, https://dev.to/usman_awan/react-concurrent-mode-optimizing-react-performance-2mln
- React Top-Level API, accessed on August 16, 2025, https://legacy.reactjs.org/docs/react-api.html
- startTransition - React, accessed on August 16, 2025, https://react.dev/reference/react/startTransition
- How does React.useDeferredValue() work internally? - JSer.dev, accessed on August 16, 2025, https://jser.dev/react/2022/01/26/how-does-react-usedeferredvalue-work/
- useTransition - React, accessed on August 16, 2025, https://react.dev/reference/react/useTransition
- Performance Optimization with React 18 Concurrent Rendering - Curiosum, accessed on August 16, 2025, https://curiosum.com/blog/performance-optimization-with-react-18-concurrent-rendering
- A Quick Start Guide to React Suspense - Refine dev, accessed on August 16, 2025, https://refine.dev/blog/react-suspense-guide/
- React Performance Hooks: Understanding useTransition and useDeferredValue, accessed on August 16, 2025, https://javascript.plainenglish.io/react-performance-hooks-understanding-usetransition-and-usedeferredvalue-af1ffec0561a
- useTransition vs useDeferredValue hook - sathyan b - Medium, accessed on August 16, 2025, https://medium.com/@sathyanvimala1995/usetransition-vs-usedeferredvalue-hook-f7c11818447e
- useDeferredValue - React, accessed on August 16, 2025, https://react.dev/reference/react/useDeferredValue
- When to use ES6 class based React components vs. ES6 React function components? [closed] - Stack Overflow, accessed on August 16, 2025, https://stackoverflow.com/questions/36097965/when-to-use-es6-class-based-react-components-vs-es6-react-function-components
- Making Sense of React Hooks. This week, Sophie Alpert and I… | by Dan Abramov | Medium, accessed on August 16, 2025, https://medium.com/@dan_abramov/making-sense-of-react-hooks-fdbde8803889
- Why are so many comments about class components, I thought we ended that debate - Hacker News, accessed on August 16, 2025, https://news.ycombinator.com/item?id=35189631
- Hooks FAQ - React, accessed on August 16, 2025, https://legacy.reactjs.org/docs/hooks-faq.html
- React Hooks vs. Classes: The Ultimate Comparison [with Code Examples] - Bitovi, accessed on August 16, 2025, https://www.bitovi.com/blog/react-hooks-vs-classes-the-ultimate-comparison
- React Hooks vs Class Components - Reddit, accessed on August 16, 2025, https://www.reddit.com/r/react/comments/13ywsq8/react_hooks_vs_class_components/
- State: A Component's Memory - React, accessed on August 16, 2025, https://react.dev/learn/state-a-components-memory
- React Hooks Best Practices: Unlocking Efficiency and Elegance | by Riya Garg - Medium, accessed on August 16, 2025, https://medium.com/womenintechnology/react-hooks-best-practices-unlocking-efficiency-and-elegance-da23f7e1418a
- Linked Lists: The Hidden Power Behind React Hooks - DEV Community, accessed on August 16, 2025, https://dev.to/talissoncosta/linked-lists-the-hidden-power-behind-react-hooks-4kfg
- Linked lists in the wild: React Hooks - DEV Community, accessed on August 16, 2025, https://dev.to/wuz/linked-lists-in-the-wild-react-hooks-3ep8
- How React Hooks Work Under the Hood: Recreating useState, useEffect, and useRef from Scratch | by Dzmitry Ihnatovich | Medium, accessed on August 16, 2025, https://medium.com/@ignatovich.dm/how-react-hooks-work-under-the-hood-recreating-usestate-useeffect-and-useref-from-scratch-65edaaa1a39b
- Can someone explain the source code inside useState to me? : r/reactjs - Reddit, accessed on August 16, 2025, https://www.reddit.com/r/reactjs/comments/1gto3vy/can_someone_explain_the_source_code_inside/
- useState - React, accessed on August 16, 2025, https://react.dev/reference/react/useState
- useEffect under the Hood - bussieck.com, accessed on August 16, 2025, https://www.bussieck.com/useeffect-under-the-hood/
- Using the Effect Hook - React, accessed on August 16, 2025, https://legacy.reactjs.org/docs/hooks-effect.html
- useEffect - React, accessed on August 16, 2025, https://react.dev/reference/react/useEffect
- Deep dive: How do React hooks really work? - Netlify, accessed on August 16, 2025, https://www.netlify.com/blog/2019/03/11/deep-dive-how-do-react-hooks-really-work/
- A Complete Guide to useEffect - Overreacted, accessed on August 16, 2025, https://overreacted.io/a-complete-guide-to-useeffect/
- Built-in React Hooks, accessed on August 16, 2025, https://react.dev/reference/react/hooks
- React Performance Optimization: Best Techniques for Faster, Smoother Apps in 2025, accessed on August 16, 2025, https://www.growin.com/blog/react-performance-optimization-2025/
- Optimizing Performance - React, accessed on August 16, 2025, https://legacy.reactjs.org/docs/optimizing-performance.html
- Performance Hooks in React - GeeksforGeeks, accessed on August 16, 2025, https://www.geeksforgeeks.org/reactjs/performance-hooks-in-react/
- Performance penalty of creating handlers on every render with react-hooks - Stack Overflow, accessed on August 16, 2025, https://stackoverflow.com/questions/53335950/performance-penalty-of-creating-handlers-on-every-render-with-react-hooks
- Introduction to debugging with React developer tools - Zipy.ai, accessed on August 16, 2025, https://www.zipy.ai/guide/react-developer-tools
- React Performance Debugging Masterclass - GitNation, accessed on August 16, 2025, https://gitnation.com/contents/react-performance-debugging-masterclass
- Debugging Performance Issues in React - Bionic Julia, accessed on August 16, 2025, https://bionicjulia.com/blog/debugging-performance-issues-in-react
- Trace why a React component is re-rendering - Stack Overflow, accessed on August 16, 2025, https://stackoverflow.com/questions/41004631/trace-why-a-react-component-is-re-rendering
- Upgrading to React 18 and common pitfalls of concurrent mode - Make WordPress, accessed on August 16, 2025, https://make.wordpress.org/core/2023/03/07/upgrading-to-react-18-and-common-pitfalls-of-concurrent-mode/
- Demystifying React 18's Concurrent Mode and Suspense: A Beginner's Guide - Medium, accessed on August 16, 2025, https://medium.com/@gadharinayan/demystifying-react-18s-concurrent-mode-and-suspense-a-beginner-s-guide-b7e29d7e4b07
- The Ultimate Guide to React: Conquering Concurrent Mode and Suspense, accessed on August 16, 2025, https://dev.to/codesensei/the-ultimate-guide-to-react-conquering-concurrent-mode-and-suspense-3ahb

浙公网安备 33010602011771号