为什么hooks不能写在条件里
# 一句话结论(先给你刻进脑子)
- Hooks 不能写在条件里,不是语法限制,
- 而是因为 React 用“调用顺序”来把 Hook 状态对齐到 Fiber。
顺序一旦不稳定,状态就会错位,且无法恢复。
# 二、Hooks 是怎么“定位自己”的?
我们已经知道:
Fiber.memoizedState
└─ hook1 → hook2 → hook3 → null
1
2
2
每次 render:
currentHook → 指向旧链表
workInProgressHook → 构建新链表
1
2
2
# 定位规则只有一个:
第 N 次调用 useX(),对应第 N 个 hook 节点
没有 key 没有名字 没有 AST 只有顺序
# 三、条件语句直接破坏了这个唯一规则
# 1️⃣ 正常情况(顺序稳定)
useState() // hook #1
useEffect() // hook #2
useMemo() // hook #3
1
2
3
2
3
每次 render 都是:
##1 → #2 → #3
1
✔ 正确
# 2️⃣ 条件 Hook(顺序不稳定)
useState() // hook #1
if (cond) {
useEffect() // ❌ 有时执行,有时不执行
}
useMemo() // hook ? ❌
1
2
3
4
5
6
7
2
3
4
5
6
7
# render #1(cond = true)
##1 useState
##2 useEffect
##3 useMemo
1
2
3
2
3
# render #2(cond = false)
##1 useState
##2 useMemo ❌ 本来是 #3
1
2
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
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
2
3
4
5
# 结果:
b可能拿到的是2setB操作的是错误的 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
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
2
3
4
5
const value = cond ? useMemo(...) : other
// ❌ 不允许(顺序不稳定)
1
2
2
# 十、终极一句话总结(给你这种原理派)
Hooks 规则不是限制开发者,
而是 Hooks 用“顺序”换来了:
- 无需标识
- 无需编译
- 可中断 render
- 极低运行时成本
这是一笔非常“React 风格”的交易。
# 如果你愿意继续往下挖(已经到最深层)
我可以继续拆:
1️⃣ 为什么 Hooks 要用全局变量(而不是参数传递) 2️⃣ 如果强行支持条件 Hook,需要改哪些核心结构 3️⃣ React 编译器(React Forget)是否可能放松这个限制
你选一个,我继续给你拆到“设计决策级”。
上次更新: 2026/01/09, 03:15:08