Open this lesson in your favourite AI. It'll walk you through the why, explain the demo, and quiz you on the try-it list.
React stores hook state in a linked list attached to each fiber node. On every render the runtime advances a cursor through that list, matching each hook call to its stored state by position. This is the mechanical reason hooks cannot be called conditionally or inside loops — skipping a call shifts every subsequent hook's cursor position, silently mapping the wrong state to the wrong hook. Understanding the call queue turns the Rules of Hooks from arbitrary convention into an inevitable consequence of the data structure.
A fiber holds an ordered list of 'memo cells'. useState, useEffect, and every other hook consumes one cell per call, in call order.
show from true to false. Note the exact console error — React reports 'Rendered fewer hooks than expected'. Read the message: it tells you exactly which render had the wrong count.console.log('hook call', n) inside each useState initializer. Wrap them in a component and toggle show. Observe how the log count changes between renders — that's the cursor advancing differently each time.if block. Confirm the error disappears. Now the cursor always advances two cells before hitting the conditional.eslint-plugin-react-hooks and add 'react-hooks/rules-of-hooks': 'error' to your ESLint config. Confirm the static analysis catches BrokenCounter before you run the code.Use these three in order. Each builds on the one before.
In one paragraph, explain the Rules of Hooks to someone new to React. Why can't hooks be called inside if statements, and what would break if they were?
Walk me through React's fiber linked-list implementation of hook state. How does the cursor advance during a render, and what exactly happens to the list when a conditional hook is skipped on the second render?
React's hook ordering constraint is a deliberate trade-off. What alternatives were considered (e.g. key-based hook lookup), and what would be gained or lost with a map-based approach that allowed conditional hooks?
import { useState } from 'react';
// CORRECT: hooks always called in the same order
function Counter({ label }: { label: string }) {
const [count, setCount] = useState(0); // cell #1
const [step, setStep] = useState(1); // cell #2
return (
<button onClick={() => setCount(c => c + step)}>
{label}: {count}
</button>
);
}
// BROKEN: conditional hook shifts every subsequent cell
function BrokenCounter({ show }: { show: boolean }) {
if (show) {
// When show flips to false this cell is skipped —
// cell #2 below becomes cell #1, corrupting the state mapping.
const [extra] = useState('extra'); // ← conditional hook
}
const [name] = useState('Alice'); // reads wrong cell when show=false
return <div>{name}</div>;
}
// FIX: call unconditionally, use conditionally
function FixedCounter({ show }: { show: boolean }) {
const [extra] = useState('extra'); // always cell #1
const [name] = useState('Alice'); // always cell #2
return <div>{show ? extra : name}</div>;
}