Rules of Hooks — Why They Exist
React's Rules of Hooks are not arbitrary restrictions — they exist because hooks rely on call order to maintain state across renders. Understanding the internal mechanism makes the rules obvious rather than mysterious.
How React Tracks Hook State Internally
// React stores hook state in a LINKED LIST attached to each fiber (component instance)
// Hooks are identified by their POSITION in the list, not by name
// On every render, React traverses the list IN ORDER:
// Render 1: [useState(0), useEffect(fn), useRef(null)]
// ^slot 0 ^slot 1 ^slot 2
// Render 2: [useState(0), useEffect(fn), useRef(null)]
// ^slot 0 ^slot 1 ^slot 2
// The slot numbers MUST match — that's why call order must be consistent
// ❌ BREAKS: hook inside an if block
function BadComponent({ isAdmin }: { isAdmin: boolean }) {
if (isAdmin) {
const [permissions, setPermissions] = useState([]); // ← sometimes slot 0, sometimes missing
}
const [name, setName] = useState(''); // ← slot 0 in some renders, slot 1 in others
// React can't track which state belongs to which hook → crash or corrupted state
}
// ✅ CORRECT: hooks always called, condition applied inside
function GoodComponent({ isAdmin }: { isAdmin: boolean }) {
const [permissions, setPermissions] = useState([]); // always slot 0
const [name, setName] = useState(''); // always slot 1
if (isAdmin) {
// use permissions here
}
}The Two Rules (and Why)
- Rule 1 — Only call hooks at the top level: never inside loops, conditions, or nested functions. React needs the call order to be identical on every render to map state to the correct slot in its internal linked list
- Rule 2 — Only call hooks from React functions: hooks work via React's rendering context. Calling them from regular JS functions, class components, or event handlers outside React will fail because the fiber context doesn't exist
- The `eslint-plugin-react-hooks` package (`react-hooks/rules-of-hooks`) statically enforces both rules at development time — this is why it's installed by default in all modern React templates
- Custom hooks are an exception to Rule 2 — a function that itself calls hooks is a custom hook; by convention it must start with `use` so ESLint can enforce the rules on it too
- Hooks in React Server Components — RSC doesn't support stateful hooks (useState, useEffect); only `use()`, `cache()`, and data-fetching patterns are available in RSC
Common Mistakes — Rules of Hooks
- Calling hooks inside try/catch — error handling doesn't change the call order concern, but eslint will flag hooks inside catch blocks; handle errors in the hook itself or via error boundaries
- Forgetting `use` prefix on custom hooks — without it, ESLint won't apply hook rules to your custom hook, allowing callers to violate the rules silently
- Thinking hooks are magic — they're just functions that access React's internal fiber through a global pointer set during render; they fail outside render because the pointer is null
Tip
Tip
Practice Rules of Hooks Why They Exist in small, isolated examples before integrating into larger projects. Breaking concepts into small experiments builds genuine understanding faster than reading alone.
Always follow Rules of Hooks. ESLint plugin catches violations. Add all dependencies. Always cleanup.
Practice Task
Note
Practice Task — (1) Write a working example of Rules of Hooks Why They Exist from scratch without looking at notes. (2) Modify it to handle an edge case (empty input, null value, or error state). (3) Share your solution in the Priygop community for feedback.
Quick Quiz
Common Mistake
Warning
A common mistake with Rules of Hooks Why They Exist is skipping edge case testing — empty inputs, null values, and unexpected data types. Always validate boundary conditions to write robust, production-ready react code.
Key Takeaways
- React's Rules of Hooks are not arbitrary restrictions — they exist because hooks rely on call order to maintain state across renders.
- Rule 1 — Only call hooks at the top level: never inside loops, conditions, or nested functions. React needs the call order to be identical on every render to map state to the correct slot in its internal linked list
- Rule 2 — Only call hooks from React functions: hooks work via React's rendering context. Calling them from regular JS functions, class components, or event handlers outside React will fail because the fiber context doesn't exist
- The `eslint-plugin-react-hooks` package (`react-hooks/rules-of-hooks`) statically enforces both rules at development time — this is why it's installed by default in all modern React templates