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

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

Glitz Ma

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

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

  • react原理

    • Vue3 effect vs react hooks
    • Fiber是什么
    • renderWithHooks是怎么挂到Fiber
      • 一、整体鸟瞰(先画流程)
      • 二、Fiber 上“挂”了什么?
        • Fiber 本身并不“保存 renderWithHooks”
      • 三、beginWork:Fiber 执行的入口
      • 四、updateFunctionComponent 做了什么?
      • 五、renderWithHooks 内部到底干了什么?
        • 一句话版
        • 1️⃣ 关键全局变量(重点)
        • 2️⃣ renderWithHooks 伪代码
      • 六、useState 是怎么“写到 Fiber 上的”?
        • Mount 阶段
        • Update 阶段
      • 七、useEffect 又是怎么“挂”的?
      • 八、对比 Vue effect
      • 九、为什么 renderWithHooks 必须在 Fiber 上?
      • 十、终极一句话总结
    • mount/update为什么能切换
    • React 更新系统的“回路闭合点”
    • 为什么hooks不能写在条件里
    • 为啥hooks要用全局变量(而不是参数传递)
  • react全家桶

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

renderWithHooks是怎么挂到Fiber

  • 先给一句话结论:
  • renderWithHooks 不是“挂载函数”,而是 render 阶段“接管 Fiber 执行权”的入口

# 一、整体鸟瞰(先画流程)

一次函数组件更新的大致路径:

scheduleUpdateOnFiber
  ↓
performConcurrentWorkOnRoot
  ↓
renderRoot
  ↓
workLoop
  ↓
performUnitOfWork
  ↓
beginWork(fiber)
  ↓
updateFunctionComponent
  ↓
renderWithHooks   👈 关键点
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

👉 renderWithHooks 就是在 beginWork 阶段被调用的


# 二、Fiber 上“挂”了什么?

# Fiber 本身并不“保存 renderWithHooks”

Fiber 上保存的是:

fiber.type          // 组件函数
fiber.memoizedState // hooks 链表
fiber.updateQueue   // effect 队列
1
2
3

👉 renderWithHooks 是执行逻辑,不是属性


# 三、beginWork:Fiber 执行的入口

简化版伪代码:

function beginWork(current, workInProgress) {
  switch (workInProgress.tag) {
    case FunctionComponent:
      return updateFunctionComponent(
        current,
        workInProgress,
        workInProgress.type,
        workInProgress.pendingProps
      )
  }
}
1
2
3
4
5
6
7
8
9
10
11

# 四、updateFunctionComponent 做了什么?

function updateFunctionComponent(
  current,
  workInProgress,
  Component,
  props
) {
  // 1. 准备上下文
  prepareToReadContext(workInProgress)

  // 2. 重点:执行组件函数
  const children = renderWithHooks(
    current,
    workInProgress,
    Component,
    props
  )

  // 3. diff children
  reconcileChildren(current, workInProgress, children)

  return workInProgress.child
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

👉 renderWithHooks 在这里“接管 render”


# 五、renderWithHooks 内部到底干了什么?

# 一句话版

  • 把当前 Fiber 设为“全局 Hook 上下文”,然后执行组件函数

# 1️⃣ 关键全局变量(重点)

let currentlyRenderingFiber = null
let workInProgressHook = null
let currentHook = null
1
2
3

⚠️ 注意:

  • Hooks 是靠全局变量 + 调用顺序工作的
  • 这也是“Hooks 不能写在条件里”的根本原因

# 2️⃣ renderWithHooks 伪代码

function renderWithHooks(
  current,
  workInProgress,
  Component,
  props
) {
  currentlyRenderingFiber = workInProgress
  workInProgress.memoizedState = null
  workInProgress.updateQueue = null

  // 区分 mount / update
  ReactCurrentDispatcher.current =
    current === null
      ? HooksDispatcherOnMount
      : HooksDispatcherOnUpdate

  const children = Component(props)

  // render 结束,清理
  currentlyRenderingFiber = null
  workInProgressHook = null
  currentHook = null

  return children
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

👉 核心:把 Fiber 暴露给 Hooks


# 六、useState 是怎么“写到 Fiber 上的”?

# Mount 阶段

function useState(initialState) {
  return mountState(initialState)
}
1
2
3
function mountState(initialState) {
  const hook = mountWorkInProgressHook()
  hook.memoizedState = initialState
  hook.queue = createUpdateQueue()

  return [hook.memoizedState, dispatch]
}
1
2
3
4
5
6
7
function mountWorkInProgressHook() {
  const hook = { memoizedState: null, next: null }

  if (workInProgressHook === null) {
    currentlyRenderingFiber.memoizedState = hook
  } else {
    workInProgressHook.next = hook
  }

  workInProgressHook = hook
  return hook
}
1
2
3
4
5
6
7
8
9
10
11
12

👉 Hook 链表挂在 fiber.memoizedState


# Update 阶段

function updateWorkInProgressHook() {
  const hook = clone(currentHook)
  workInProgressHook.next = hook
  currentHook = currentHook.next
  workInProgressHook = hook
}
1
2
3
4
5
6

👉 靠调用顺序对齐


# 七、useEffect 又是怎么“挂”的?

function mountEffect(create, deps) {
  const hook = mountWorkInProgressHook()

  const effect = {
    create,
    deps,
    destroy: null
  }

  pushEffect(effect)
}
1
2
3
4
5
6
7
8
9
10
11
function pushEffect(effect) {
  const fiber = currentlyRenderingFiber
  fiber.flags |= Passive
  fiber.updateQueue.push(effect)
}
1
2
3
4
5
  • 👉 effect 不在 hook 链表里执行
  • 👉 而是挂在 Fiber.updateQueue,commit 阶段跑

# 八、对比 Vue effect

Vue3 React
activeEffect currentlyRenderingFiber
effect(fn) renderWithHooks
dep → effect fiber.memoizedState
scheduler Scheduler + commit

# 九、为什么 renderWithHooks 必须在 Fiber 上?

因为:

  1. Hooks 状态必须 跟组件实例绑定
  2. render 可中断 / 重跑
  3. Fiber 是唯一稳定载体
  • Hooks 不是组件私有变量,而是 Fiber 状态机的一部分

# 十、终极一句话总结

  • renderWithHooks 的本质:
  • 在 render 阶段,把 Fiber 暂时“提升”为全局上下文,让 Hooks 有地方读写状态
上次更新: 2026/01/09, 03:54:23
Fiber是什么
mount/update为什么能切换

← Fiber是什么 mount/update为什么能切换→

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