Skip to content
Created: September 24, 2025

Conclusion ​

  • Hooks cannot be written in conditions, it is not a grammatical restriction,
  • But because React uses "call order" to align Hook state to Fiber.

Once the order is unstable, The state will be dislocated and cannot be restored.

2. How does Hooks “position itself”? ​

We already know:

txt
Fiber.memoizedState
  └─ hook1 → hook2 → hook3 → null

Each render:

txt
currentHook  → 指向旧链表
workInProgressHook → 构建新链表

There is only one positioning rule: ​

The Nth call to useX() corresponds to the Nth hook node

no key no name No AST only order

3. Conditional statements directly destroy this unique rule. ​

1️⃣ Normal situation (stable order) ​

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

Each render is:

txt
##1 → #2 → #3

✔ Correct

2️⃣ Conditional Hook (the order is unstable) ​

ts
useState()       // hook #1

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

useMemo()        // hook ? ❌

render #1(cond = true) ​

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

render #2(cond = false) ​

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

4. Source code level dislocation process (the part you care about) ​

updateWorkInProgressHook simplified version: ​

ts
function updateWorkInProgressHook() {
  const nextCurrentHook = currentHook.next

  const newHook = clone(nextCurrentHook)

  currentHook = nextCurrentHook
  workInProgressHook.next = newHook
  workInProgressHook = newHook
}

React By default you will call the next Hook every time.

When you skip a Hook: ​

  • currentHook is not moving forward
  • workInProgressHook but keeps moving forward
  • The linked list is directly misaligned

5. What will be caused by misalignment?(real disaster) ​

Misalignment is not as simple as "executing one less effect" ​

Example: ​
ts
const [a, setA] = useState(1)
if (cond) {
  useState(2)
}
const [b, setB] = useState(3)

Result: ​

  • b might get 2
  • setB operates on the wrong queue
  • update lane wrongly tied fiber
  • ❌ Behavior is unpredictable

👉 This is no longer a bug, it’s state pollution

6. Why doesn’t React use “name/key” to mark Hook? ​

You will definitely think of this issue at this level.

Theoretically possible, but the cost is extremely high: ​

1️⃣ Each render requires:

  • Parse AST
  • Create key mapping
  • diffHook structure

2️⃣ render is no longer:

txt
Component(props) → JSX

And becomes:

txt
Component → 解析 → 调度

👉 This directly negates Fiber’s “rerunable render” design

7. Why doesn’t Vue have this problem? ​

Vue effect: ​

ts
watchEffect(() => {
  if (cond) {
    console.log(a.value)
  }
})

✔ No problem because:

  • Effect is dynamic dependency collection
  • not in order
  • Regardless of location
  • Not relying on render

👉 The inevitable result of the two models

8. StrictMode dual execution, why hasn’t it exploded yet? ​

Because:

  • mount → unmount → mount
  • Complete sequence every time
  • Not “one less Hook”

9. The real rule (not “can’t write if”) ​

  • ❌ Misunderstanding: Hooks cannot be written in if
  • ✅ Correct understanding: The calling sequence of Hooks must be completely consistent in each render

So these are allowed:

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

10. The ultimate one-sentence summary (for you this kind of principle school) ​

Hooks rules are not restrictive to developers,

Instead, Hooks uses "sequence" in exchange for:

  • No identification required
  • No need to compile
  • Interruptible render
  • Extremely low runtime cost

This is a very "React-y" deal.