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
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
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
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
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
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
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
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
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
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
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
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
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 上?
因为:
- Hooks 状态必须 跟组件实例绑定
- render 可中断 / 重跑
- Fiber 是唯一稳定载体
- Hooks 不是组件私有变量,而是 Fiber 状态机的一部分
# 十、终极一句话总结
- renderWithHooks 的本质:
- 在 render 阶段,把 Fiber 暂时“提升”为全局上下文,让 Hooks 有地方读写状态
上次更新: 2026/01/09, 03:54:23