Capítulo 116 de 116

Chapter 116: Rules of Hooks

Core Idea

Hooks must be called in the exact same order on every render of a given component — only at the top level (never inside conditions, loops, or nested functions) and only from React function components or other custom Hooks — because React associates each Hook's internal state with its call position, not with any name or identifier.

Key Concepts

  • Only call Hooks at the top level. Never inside if/for/nested helper functions — a Hook called conditionally means its position in the call sequence can differ between renders, and React has no way to reliably match that render's useState(0) call to the correct previously-stored state.
  • Only call Hooks from React functions: a component function, or another custom Hook (Ch 41) — never from a plain JavaScript function, a class method, an event handler passed around as a callback, or top-level module code.
  • Why order matters mechanically: React tracks Hook state internally as an ordered list per component instance, matched to the Nth Hook call in render order — call order is the only identifier React has, since Hook calls have no explicit name/key the way state variables or props do.
  • The fix for "I need a Hook conditionally": restructure — extract the conditional logic into a separate component that either renders or doesn't (so the Hook itself is always called unconditionally within that component), rather than trying to skip a Hook call based on a condition.
  • use (Ch 91) is the sole documented exception — it's explicitly designed to tolerate conditional/loop calls, unlike every other built-in and custom Hook, because of how it integrates with Suspense and Context reading rather than persistent per-instance state.
  • Enforced mechanically by the linter: the rules-of-hooks ESLint rule (Ch 47) catches violations of this rule automatically — treat its errors as non-negotiable, not warnings to suppress.

Code Examples

// ❌ Wrong: conditional Hook call — breaks call-order matching
if (isLoggedIn) {
  const [name, setName] = useState('');
}

// ✅ Correct: Hook always called; the condition affects what's rendered, not whether the Hook runs
const [name, setName] = useState('');
return isLoggedIn ? <Greeting name={name} /> : null;
  • What it demonstrates: moving the conditional logic around the Hook call (affecting the render output) rather than around the call itself (affecting whether the Hook executes at all).

Key Takeaways

  1. "Top level only, same order every render" is a mechanical requirement of how React tracks Hook state, not an arbitrary style rule.
  2. When a Hook feels like it should be conditional, restructure — either extract a component that renders or doesn't, or call the Hook unconditionally and use its result conditionally.
  3. use (Ch 91) is a deliberate, singular exception — don't generalize its conditional-call flexibility to any other Hook.

Connects To

  • Ch 21 (State: A Component's Memory): the underlying per-call-position state-tracking model this rule protects.
  • Ch 91 (use): the one Hook explicitly exempt from this rule.
  • Ch 47 (ESLint Rules Reference): the rules-of-hooks lint rule enforcing this automatically.