Skip to content
  • renderWithHooks 不是“挂载函数”,而是 render 阶段“接管 Fiber 执行权”的入口

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

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

txt
scheduleUpdateOnFiber

performConcurrentWorkOnRoot

renderRoot

workLoop

performUnitOfWork

beginWork(fiber)

updateFunctionComponent

renderWithHooks   👈 关键点

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

二、Fiber 上“挂”了什么?

Fiber 本身并不“保存 renderWithHooks”

Fiber 上保存的是:

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

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

三、beginWork:Fiber 执行的入口

简化版伪代码:

ts
function beginWork(current, workInProgress) {
  switch (workInProgress.tag) {
    case FunctionComponent:
      return updateFunctionComponent(
        current,
        workInProgress,
        workInProgress.type,
        workInProgress.pendingProps
      )
  }
}

四、updateFunctionComponent 做了什么?

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 在这里“接管 render”

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

一句话版

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

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

ts
let currentlyRenderingFiber = null
let workInProgressHook = null
let currentHook = null

⚠️ 注意:

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

2️⃣ renderWithHooks 伪代码

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
}

👉 核心:把 Fiber 暴露给 Hooks

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

Mount 阶段

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 链表挂在 fiber.memoizedState

Update 阶段

ts
function updateWorkInProgressHook() {
  const hook = clone(currentHook)
  workInProgressHook.next = hook
  currentHook = currentHook.next
  workInProgressHook = hook
}

👉 靠调用顺序对齐

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

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 不在 hook 链表里执行
  • 👉 而是挂在 Fiber.updateQueue,commit 阶段跑

八、对比 Vue effect

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

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

因为:

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

十、renderWithHooks 的本质:

  • 在 render 阶段,把 Fiber 暂时“提升”为全局上下文,让 Hooks 有地方读写状态