The Problem
React Hooks must be called in the exact same order on every render. Calling a hook inside a conditional, loop, or nested function breaks this rule and causes state mismatches that corrupt your component's internal state.
Symptoms
- Error: "Rendered more hooks than during the previous render"
- Error: "Rendered fewer hooks than expected"
- State values are swapped between different useState calls
- Custom hook returns undefined or wrong data
- Works in some code paths but crashes in others
Real Error Message
``` Error: Rendered more hooks than during the previous render. at MyComponent at App
Warning: React has detected a change in the order of Hooks called by MyComponent. This will lead to bugs and errors if not fixed. ```
What NOT to Do
```javascript // BAD: Hook inside conditional function UserPanel({ isAdmin }) { const [name, setName] = useState('');
if (isAdmin) { const [permissions, setPermissions] = usePermissions(); // WRONG! }
return <div>{name}</div>; }
// BAD: Hook inside loop function FormList({ fields }) { fields.map(field => { const [value, setValue] = useState(field.default); // WRONG! return <input value={value} />; }); }
// BAD: Hook after early return function Dashboard({ user }) { if (!user) return <Login />; const [settings, setSettings] = useSettings(); // WRONG! Not called on all paths } ```
How to Fix It
Fix 1: Move Hook to Top Level
```javascript function UserPanel({ isAdmin }) { const [name, setName] = useState(''); const [permissions, setPermissions] = usePermissions(); // Always called
return ( <div> {name} {isAdmin && <PermissionsEditor permissions={permissions} />} </div> ); } ```
Fix 2: Move Hook Inside the Conditional Component
```javascript function AdminPanel() { const [permissions, setPermissions] = usePermissions(); // Safe: component always renders return <PermissionsEditor permissions={permissions} />; }
function UserPanel({ isAdmin }) { const [name, setName] = useState(''); return ( <div> {name} {isAdmin && <AdminPanel />} </div> ); } ```
Fix 3: Use Hook Return Value Conditionally
```javascript function Dashboard({ user }) { const [settings, setSettings] = useSettings(); // Always called
if (!user) return <Login />;
// Use settings conditionally, but hook is always called return <div>{settings.theme}</div>; } ```
Fix 4: Custom Hook with Conditional Logic Inside
```javascript // The custom hook can have conditionals internally function useConditionalPermissions(isAdmin) { const [permissions] = useState(() => isAdmin ? fetchAdminPermissions() : fetchUserPermissions() ); return permissions; // Hook is always called at top level }
function UserPanel({ isAdmin }) { const permissions = useConditionalPermissions(isAdmin); // Safe return <div>{permissions}</div>; } ```
Enforcement
Install and configure the ESLint plugin:
npm install eslint-plugin-react-hooks{
"plugins": ["react-hooks"],
"rules": {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn"
}
}This catches hook violations at lint time before the code ever runs.