Glittering's blog Glittering's blog
Home
  • 学习手册

    • 《TypeScript教程》
    • 《Git》
    • 《Vite》
    • 《Vue3》
    • 《React18》
    • 《CSS》
    • 《Tailwind CSS》
    • 《JavaScript教程》
    • 《ES6 教程》
    • 《TypeScript 从零实现 axios》
  • 技术文档
  • 算法
  • 工作总结
  • 实用技巧
  • collect
About
  • Classification
  • Label
GitHub (opens new window)

Glitz Ma

前端开发工程师
Home
  • 学习手册

    • 《TypeScript教程》
    • 《Git》
    • 《Vite》
    • 《Vue3》
    • 《React18》
    • 《CSS》
    • 《Tailwind CSS》
    • 《JavaScript教程》
    • 《ES6 教程》
    • 《TypeScript 从零实现 axios》
  • 技术文档
  • 算法
  • 工作总结
  • 实用技巧
  • collect
About
  • Classification
  • Label
GitHub (opens new window)
  • react基础

  • react原理

    • Vue3 effect vs react hooks
    • Fiber是什么
    • renderWithHooks是怎么挂到Fiber
    • mount/update为什么能切换
    • React 更新系统的“回路闭合点”
      • 一句话总览(先把全链路记住)
      • 一、setState 的“魔法”:它一开始就绑定了 Fiber
        • 真相是:👇
      • 二、dispatchSetState:更新的真正入口
      • 三、lane 是什么?(对 Vue scheduler 的升级)
        • Vue:
        • React:
        • lane 本质
        • requestUpdateLane 做了什么?
      • 四、enqueueUpdate:更新“挂”在哪里?
        • 对 Hooks(useState)
      • 五、scheduleUpdateOnFiber:真正“回到 Fiber”
        • markUpdateLaneFromFiberToRoot
      • 六、为什么一定要“回到 root”?
      • 七、Scheduler 什么时候真正 render?
      • 八、render 阶段:update 被消费
      • 九、为什么 React 更新“绕一大圈”?
        • React 必须支持:
      • 十、Vue effect vs React update(终极对照)
      • 十一、终极一句话总结(这条非常重要)
    • 为什么hooks不能写在条件里
    • 为啥hooks要用全局变量(而不是参数传递)
  • react全家桶

  • 《React18》学习笔记
  • react原理
mamingjuan
2025-09-23
目录

React 更新系统的“回路闭合点”

一次 setState,是怎么绕一大圈,又精确“回到”对应 Fiber 的?

setState → dispatchAction → lane → Fiber 这条链完整拆开。


# 一句话总览(先把全链路记住)

setState
 → dispatchSetState
 → requestUpdateLane
 → enqueueUpdate (hook.queue)
 → scheduleUpdateOnFiber
 → markRootUpdated
 → renderRoot
 → beginWork(fiber)
1
2
3
4
5
6
7
8
  • 👉 关键点:Fiber 在 setState 时就已经被“捕获”了

# 一、setState 的“魔法”:它一开始就绑定了 Fiber

你先回忆 useState 返回的 setState:

const [state, setState] = useState(0)
1

# 真相是:👇

const dispatch = dispatchSetState.bind(
  null,
  currentlyRenderingFiber,
  hook.queue
)
1
2
3
4
5
  • 👉 setState 是一个闭包
  • 👉 它早就记住了:
  • fiber
  • updateQueue

⚠️ 重点结论:

setState 不需要“查 Fiber”,因为它从一开始就带着 Fiber


# 二、dispatchSetState:更新的真正入口

function dispatchSetState(fiber, queue, action) {
  const lane = requestUpdateLane(fiber)

  const update = {
    lane,
    action,
    next: null,
  }

  enqueueUpdate(queue, update)
  scheduleUpdateOnFiber(fiber, lane)
}
1
2
3
4
5
6
7
8
9
10
11
12

这一步发生了三件关键事:

  • 1️⃣ 决定优先级(lane)
  • 2️⃣ 把 update 放进 hook.queue
  • 3️⃣ 通知调度系统:这个 Fiber 要更新

# 三、lane 是什么?(对 Vue scheduler 的升级)

# Vue:

trigger → jobQueue
1

# React:

update → lane → root.pendingLanes
1
# lane 本质

lane = 用 bit 表示的优先级通道

SyncLane        = 0b0001
InputLane       = 0b0010
TransitionLane  = 0b0100
IdleLane        = 0b1000
1
2
3
4
  • 👉 多个 lane 可同时存在
  • 👉 Scheduler 每次选最高优先级

# requestUpdateLane 做了什么?

function requestUpdateLane(fiber) {
  if (isSync) return SyncLane
  if (isTransition) return TransitionLane
  return DefaultLane
}
1
2
3
4
5
  • 点击 → Sync
  • startTransition → Transition
  • 普通 setState → Default

# 四、enqueueUpdate:更新“挂”在哪里?

# 对 Hooks(useState)

queue.pending = update
1

更新结构:

Fiber
 └─ memoizedState
     └─ hook
         └─ queue
             └─ pending → update
1
2
3
4
5
  • 👉 update 并不在 Fiber 本身
  • 👉 在 hook.queue 上

# 五、scheduleUpdateOnFiber:真正“回到 Fiber”

function scheduleUpdateOnFiber(fiber, lane) {
  const root = markUpdateLaneFromFiberToRoot(fiber, lane)
  ensureRootIsScheduled(root)
}
1
2
3
4

# markUpdateLaneFromFiberToRoot

fiber
  ↑ return
parent
  ↑ return
...
root
1
2
3
4
5
6

沿着 fiber.return 一路向上:

parent.lanes |= lane
root.pendingLanes |= lane
1
2
  • 👉 这一步,Fiber 成功“通知”了整棵树

# 六、为什么一定要“回到 root”?

因为:

  • 调度是 root 级别的
  • render 是从 root 开始
  • Scheduler 不关心某个 Fiber
root.pendingLanes != 0
 → root 需要 render
1
2

# 七、Scheduler 什么时候真正 render?

performConcurrentWorkOnRoot(root)
1
  • 1️⃣ 选最高优先级 lane

  • 2️⃣ renderRoot(lane)

  • 3️⃣ workLoop

  • 4️⃣ beginWork(root.child)

  • 👉 最终又回到最初那个 Fiber


# 八、render 阶段:update 被消费

当 render 到对应 Fiber:

updateFunctionComponent
 → renderWithHooks
 → updateState
1
2
3
const pending = hook.queue.pending
1
  • 消费 update
  • 计算新 state
  • 写入 hook.memoizedState
  • 👉 闭环完成

# 九、为什么 React 更新“绕一大圈”?

你可能会觉得:

“Vue trigger effect 多直接啊?”

原因是:

# React 必须支持:

  • 并发
  • 中断
  • 优先级
  • 丢弃 render
  • 👉 所以 update 必须是“可延迟消费的描述”,而不是立即执行

# 十、Vue effect vs React update(终极对照)

Vue React
setter 触发 dispatchAction
dep 定位 闭包记住 Fiber
scheduler lane
effect 执行 render 重跑
精确更新 可中断更新

# 十一、终极一句话总结(这条非常重要)

setState 能“回到 Fiber”,不是因为 React 很聪明, 而是因为 setState 从诞生那一刻起,就已经“记住了它来自哪个 Fiber”。

上次更新: 2026/01/21, 04:36:31
mount/update为什么能切换
为什么hooks不能写在条件里

← mount/update为什么能切换 为什么hooks不能写在条件里→

Copyright © 2015-2026 Glitz Ma
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式