Skip to content
Created: September 18, 2025
  • 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 ​

Vue3React
activeEffectcurrentlyRenderingFiber
effect(fn)renderWithHooks
dep → effectfiber.memoizedState
schedulerScheduler + commit

9. Why does renderWithHooks have to be on Fiber? ​

Because:

  1. Hooks state must be bound to component instance
  2. render can be interrupted/rerun
  3. 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.