- renderWithHooks is not a "mounting function", but the entrance to "take over Fiber execution rights" in the render phase
1. Overall bird’s eye view (draw the process first)
The approximate path of a function component update:
txt
scheduleUpdateOnFiber
↓
performConcurrentWorkOnRoot
↓
renderRoot
↓
workLoop
↓
performUnitOfWork
↓
beginWork(fiber)
↓
updateFunctionComponent
↓
renderWithHooks 👈 关键点👉 renderWithHooks is called in the beginWork stage
2. What is “hung” on Fiber?
Fiber itself does not "save renderWithHooks"
What is saved on Fiber is:
ts
fiber.type // 组件函数
fiber.memoizedState // hooks 链表
fiber.updateQueue // effect 队列👉 renderWithHooks is execution logic, not attributes
3. beginWork: the entry point for Fiber execution
Simplified version of pseudocode:
ts
function beginWork(current, workInProgress) {
switch (workInProgress.tag) {
case FunctionComponent:
return updateFunctionComponent(
current,
workInProgress,
workInProgress.type,
workInProgress.pendingProps
)
}
}4. What does updateFunctionComponent do?
ts
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
}👉 renderWithHooks "takes over render" here
5. What is done internally in renderWithHooks?
one sentence version
- Set the current Fiber as the "global Hook context" and then execute the component function
1️⃣ Key global variables (key points)
ts
let currentlyRenderingFiber = null
let workInProgressHook = null
let currentHook = null⚠️ NOTE:
- Hooks work relying on global variables + calling order
- This is also the fundamental reason why "Hooks cannot be written in conditions"
2️⃣ renderWithHooks pseudo code
ts
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
}👉 Core: Exposing Fiber to Hooks
6. How is useState "written to Fiber"?
Mount stage
ts
function useState(initialState) {
return mountState(initialState)
}ts
function mountState(initialState) {
const hook = mountWorkInProgressHook()
hook.memoizedState = initialState
hook.queue = createUpdateQueue()
return [hook.memoizedState, dispatch]
}ts
function mountWorkInProgressHook() {
const hook = { memoizedState: null, next: null }
if (workInProgressHook === null) {
currentlyRenderingFiber.memoizedState = hook
} else {
workInProgressHook.next = hook
}
workInProgressHook = hook
return hook
}👉 Hook linked list is hung in fiber.memoizedState
Update stage
ts
function updateWorkInProgressHook() {
const hook = clone(currentHook)
workInProgressHook.next = hook
currentHook = currentHook.next
workInProgressHook = hook
}👉 Aligned by calling order
7. How does useEffect "hang"?
ts
function mountEffect(create, deps) {
const hook = mountWorkInProgressHook()
const effect = {
create,
deps,
destroy: null
}
pushEffect(effect)
}ts
function pushEffect(effect) {
const fiber = currentlyRenderingFiber
fiber.flags |= Passive
fiber.updateQueue.push(effect)
}- 👉 effect is not executed in the hook list
- 👉 Instead hang on Fiber.updateQueue and run in the commit phase
8. Compare Vue effect
| Vue3 | React |
|---|---|
| activeEffect | currentlyRenderingFiber |
| effect(fn) | renderWithHooks |
| dep → effect | fiber.memoizedState |
| scheduler | Scheduler + commit |
9. Why does renderWithHooks have to be on Fiber?
Because:
- Hooks state must be bound to component instance
- render can be interrupted/rerun
- Fiber is the only stable carrier
- Hooks are not component private variables, but part of the Fiber state machine
10. The essence of renderWithHooks:
- In the render phase, Fiber is temporarily "promoted" to the global context so that Hooks have a place to read and write status.