Explain why React Hooks must be called unconditionally and in the same order, how React internally matches Hook calls to state slots, and what breaks when that order changes. Hooks rely on call order, so violations cause subtle bugs. Test with lint rules and ensure stable hook order.
Use this React interview question to rehearse a quick answer, common mistake, follow-up, and production pitfall.
Why do Hooks have rules? What breaks if you call Hooks conditionally?Frontend interview answer
This React interview question tests whether you can explain React Hooks have rules, connect it to production trade-offs, and handle common follow-up questions.
- React Hooks have rules explanation without falling back to memorized docs wording
- Hooks and State reasoning, edge cases, and production failure modes
- How you would answer the most likely React interview follow-up
Short answer
Hooks have rules because React does not identify Hooks by name. It identifies them by call order. If you call Hooks conditionally, the order changes between renders, and React starts attaching the wrong state/effect/memo to the wrong Hook call.
The mental model
Internally, React stores Hooks for a component in something like an array and uses a pointer while rendering:useState → slot 0useEffect → slot 1useMemo → slot 2
On every render, React assumes: "The first Hook call is the same Hook as last time. The second Hook call is the same Hook as last time."
So the only thing that matters is: the order of Hook calls must be identical on every render.
function Example({ showExtra }) {
const [count, setCount] = useState(0); // Hook slot 0
if (showExtra) {
const [flag, setFlag] = useState(false); // Hook slot 1 (sometimes!)
}
const [name, setName] = useState('Alice'); // Hook slot 1 or 2 🤯
}
What breaks here?
When showExtra is false, the second useState is skipped. That means the name state now occupies slot 1 instead of slot 2.
On the next render when showExtra becomes true, React thinks:
• slot 1 = flag state
• slot 2 = name state
But previously:
• slot 1 was actually name state 😬
So now React mixes up states. Values jump between variables. Effects attach to the wrong logic. Everything becomes silently wrong.
Rule | Why it exists | What breaks if you violate it |
|---|---|---|
Only call Hooks at the top level | Guarantees stable call order | State/effects shift between variables |
Never call Hooks conditionally | Ensures same Hooks run every render | Hooks get mismatched to wrong slots |
Only call Hooks from components or custom Hooks | Ensures React controls render lifecycle | React cannot track Hook state correctly |
Important insight
This is not a "style rule". This is a fundamental constraint of how Hooks are implemented. React could have designed a more complex system using keys or IDs, but that would make Hooks slower, more complex, and harder to optimize.
How you should write conditional logic instead
Call Hooks unconditionally, and put the condition inside the Hook logic:
function Example({ showExtra }) {
const [count, setCount] = useState(0);
const [flag, setFlag] = useState(false);
const [name, setName] = useState('Alice');
useEffect(() => {
if (!showExtra) return;
// conditional behavior goes here
}, [showExtra]);
}
Production-flavored example: feature flag / modal toggle
The real bug is not academic. It happens when a feature flag, modal, or debug panel conditionally introduces a Hook and shifts every later slot.
// ❌ When showAdvanced flips, the later hooks move to different slots
function BillingPanel({ showAdvanced }) {
const [plan, setPlan] = useState('pro');
if (showAdvanced) {
const [coupon, setCoupon] = useState('');
}
const [notes, setNotes] = useState('');
return <Panel />;
}
// ✅ Keep hook order stable; move the condition inside the hook or JSX
function BillingPanelFixed({ showAdvanced }) {
const [plan, setPlan] = useState('pro');
const [coupon, setCoupon] = useState('');
const [notes, setNotes] = useState('');
useEffect(() => {
if (!showAdvanced) return;
validateCoupon(coupon);
}, [showAdvanced, coupon]);
return showAdvanced ? <Advanced coupon={coupon} /> : <Basic notes={notes} />;
}
Early return: wrong vs right
Returning early before later Hooks is the same class of bug as a conditional Hook call. React still expects the same hook index progression on every render.
// ❌ Early return skips later hooks on some renders
function Profile({ user }) {
const [tab, setTab] = useState('overview');
if (!user) return <Spinner />;
const [draft, setDraft] = useState(user.name);
return <Editor draft={draft} />;
}
// ✅ Put all hooks first, then branch in render output
function ProfileFixed({ user }) {
const [tab, setTab] = useState('overview');
const [draft, setDraft] = useState('');
if (!user) return <Spinner />;
return <Editor draft={draft} />;
}
Hook index mental model
You can think of React as replaying a hook index from top to bottom on every render: slot 0, slot 1, slot 2, and so on. If one render skips slot 1 because a branch changed, every later state/effect/memo lookup shifts and React starts attaching the wrong data to the wrong Hook.
Interview framing
Say it like this:
"React tracks Hooks by call order, not by name. If you call Hooks conditionally, the order changes between renders, so React attaches state and effects to the wrong Hook. The Rules of Hooks exist to guarantee stable ordering."
Practical guardrail
Lint rules matter here because the failure mode is often silent until a branch toggles in production. Keep hooks at the top level, do conditional behavior inside an effect/callback, and let eslint-plugin-react-hooks catch branch-specific mistakes before they become state-slot corruption bugs.
Hooks must be called in the same order on every render. React relies on call order to map Hook calls to internal state slots. Conditional Hooks break this mapping, causing state and effects to shift between variables and produce unpredictable bugs.
Use this as one explanation rep, then continue with the React interview questions cluster or a guided prep path.