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
    • mount/update为什么能切换
    • React 更新系统的“回路闭合点”
    • 为什么hooks不能写在条件里
      • 一句话结论(先给你刻进脑子)
      • 二、Hooks 是怎么“定位自己”的?
        • 定位规则只有一个:
      • 三、条件语句直接破坏了这个唯一规则
        • 1️⃣ 正常情况(顺序稳定)
        • 2️⃣ 条件 Hook(顺序不稳定)
        • render #1(cond = true)
        • render #2(cond = false)
      • 四、源码级错位过程(你关心的部分)
        • updateWorkInProgressHook 简化版:
        • 当你跳过一个 Hook:
      • 五、错位会导致什么?(真实灾难)
        • 错位不是“少执行一个 effect”这么简单
        • 例子:
        • 结果:
      • 六、为什么 React 不用“名字 / key”来标记 Hook?
        • 理论上可以,但代价极高:
      • 七、为什么 Vue 没这个问题?
        • Vue effect:
      • 八、StrictMode 双执行,为什么还没炸?
      • 九、真正的规则(不是“不能写 if”)
      • 十、终极一句话总结(给你这种原理派)
        • 如果你愿意继续往下挖(已经到最深层)
    • 为啥hooks要用全局变量(而不是参数传递)
  • react全家桶

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

为什么hooks不能写在条件里

# 一句话结论(先给你刻进脑子)

  • Hooks 不能写在条件里,不是语法限制,
  • 而是因为 React 用“调用顺序”来把 Hook 状态对齐到 Fiber。

顺序一旦不稳定,状态就会错位,且无法恢复。


# 二、Hooks 是怎么“定位自己”的?

我们已经知道:

Fiber.memoizedState
  └─ hook1 → hook2 → hook3 → null
1
2

每次 render:

currentHook  → 指向旧链表
workInProgressHook → 构建新链表
1
2

# 定位规则只有一个:

第 N 次调用 useX(),对应第 N 个 hook 节点

没有 key 没有名字 没有 AST 只有顺序


# 三、条件语句直接破坏了这个唯一规则

# 1️⃣ 正常情况(顺序稳定)

useState()   // hook #1
useEffect()  // hook #2
useMemo()   // hook #3
1
2
3

每次 render 都是:

##1 → #2 → #3
1

✔ 正确


# 2️⃣ 条件 Hook(顺序不稳定)

useState()       // hook #1

if (cond) {
  useEffect()    // ❌ 有时执行,有时不执行
}

useMemo()        // hook ? ❌
1
2
3
4
5
6
7

# render #1(cond = true)

##1 useState
##2 useEffect
##3 useMemo
1
2
3

# render #2(cond = false)

##1 useState
##2 useMemo   ❌ 本来是 #3
1
2

# 四、源码级错位过程(你关心的部分)

# updateWorkInProgressHook 简化版:

function updateWorkInProgressHook() {
  const nextCurrentHook = currentHook.next

  const newHook = clone(nextCurrentHook)

  currentHook = nextCurrentHook
  workInProgressHook.next = newHook
  workInProgressHook = newHook
}
1
2
3
4
5
6
7
8
9

React 默认你每次都会调用下一个 Hook。

# 当你跳过一个 Hook:

  • currentHook 没前进
  • workInProgressHook 却继续前进
  • 链表直接错位

# 五、错位会导致什么?(真实灾难)

# 错位不是“少执行一个 effect”这么简单

# 例子:
const [a, setA] = useState(1)
if (cond) {
  useState(2)
}
const [b, setB] = useState(3)
1
2
3
4
5

# 结果:

  • b 可能拿到的是 2
  • setB 操作的是错误的 queue
  • update lane 错绑 Fiber
  • ❌ 行为不可预测

👉 这已经不是 bug,是状态污染


# 六、为什么 React 不用“名字 / key”来标记 Hook?

你这个层级一定会想到这个问题。

# 理论上可以,但代价极高:

1️⃣ 每次 render 要:

  • 解析 AST
  • 建立 key 映射
  • diff Hook 结构

2️⃣ render 不再是:

Component(props) → JSX
1

而变成:

Component → 解析 → 调度
1

👉 这直接否定 Fiber 的“可重跑 render”设计


# 七、为什么 Vue 没这个问题?

# Vue effect:

watchEffect(() => {
  if (cond) {
    console.log(a.value)
  }
})
1
2
3
4
5

✔ 没问题,因为:

  • effect 是动态依赖收集
  • 不靠顺序
  • 不靠位置
  • 不靠 render

👉 两种模型的必然结果


# 八、StrictMode 双执行,为什么还没炸?

因为:

  • mount → unmount → mount
  • 每次都是完整顺序
  • 不是“少一次 Hook”

# 九、真正的规则(不是“不能写 if”)

  • ❌ 错误理解:Hooks 不能写在 if
  • ✅ 正确理解:Hooks 的调用顺序必须在每次 render 中完全一致

所以这些是 允许的:

useEffect(() => {
  if (cond) {
    // 可以
  }
}, [cond])
1
2
3
4
5
const value = cond ? useMemo(...) : other
// ❌ 不允许(顺序不稳定)
1
2

# 十、终极一句话总结(给你这种原理派)

Hooks 规则不是限制开发者,

而是 Hooks 用“顺序”换来了:

  • 无需标识
  • 无需编译
  • 可中断 render
  • 极低运行时成本

这是一笔非常“React 风格”的交易。


# 如果你愿意继续往下挖(已经到最深层)

我可以继续拆:

1️⃣ 为什么 Hooks 要用全局变量(而不是参数传递) 2️⃣ 如果强行支持条件 Hook,需要改哪些核心结构 3️⃣ React 编译器(React Forget)是否可能放松这个限制

你选一个,我继续给你拆到“设计决策级”。

上次更新: 2026/01/09, 03:15:08
React 更新系统的“回路闭合点”
为啥hooks要用全局变量(而不是参数传递)

← React 更新系统的“回路闭合点” 为啥hooks要用全局变量(而不是参数传递)→

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