---
name: following-the-rules-of-hooks
description: Fix React Rules of Hooks violations - conditional calls, hooks in loops/callbacks/classes
version: 1.0.0
---
# Rules of Hooks
React enforces two invariants on Hook usage. Violating these causes state corruption and unpredictable behavior.
## The Rules
1. **Top-level only** - Never call Hooks inside loops, conditions, nested functions, or try/catch/finally
2. **React functions only** - Call Hooks exclusively from function components or custom Hooks
**Why:** Consistent call order across renders; conditional/dynamic invocation breaks state tracking.
## Valid Hook Locations
✅ Top level of function components
✅ Top level of custom Hooks (`use*` functions)
```javascript
function Counter() {
const [count, setCount] = useState(0);
return
{count}
;
}
function useWindowWidth() {
const [width, setWidth] = useState(window.innerWidth);
return width;
}
```
## Common Violations
| Violation | Why Invalid | Fix |
|-----------|-------------|-----|
| Inside if/else | Skipped on some renders | Move to top; use conditional rendering |
| Inside loops | Variable call count | Move to top; manage array state |
| After early return | Unreachable on some paths | Move Hook before return |
| In event handlers | Called outside render | Move to top; use state from closure |
| In class components | Classes don't support Hooks | Convert to function component |
| Inside callbacks | Nested function context | Move Hook to top level |
## Common Fixes
### Conditional Hooks
❌ **Wrong:**
```javascript
function Profile({ userId }) {
if (userId) {
const user = useUser(userId);
}
}
```
✅ **Right:**
```javascript
function Profile({ userId }) {
const user = useUser(userId);
if (!userId) return null;
return
{user.name}
;
}
```
**Pattern:** Always call Hook, use conditional rendering for output.
### Hooks in Loops
❌ **Wrong:**
```javascript
function List({ items }) {
return items.map(item => {
const [selected, setSelected] = useState(false);
return ;
});
}
```
✅ **Right:**
```javascript
function List({ items }) {
const [selected, setSelected] = useState({});
return items.map(item => (
setSelected(s => ({...s, [item.id]: !s[item.id]}))}
/>
));
}
```
**Pattern:** Single Hook managing collection, not per-item Hooks.
### Hooks in Event Handlers
❌ **Wrong:**
```javascript
function Form() {
function handleSubmit() {
const [loading, setLoading] = useState(false);
setLoading(true);
}
return ;
}
```
✅ **Right:**
```javascript
function Form() {
const [loading, setLoading] = useState(false);
function handleSubmit() {
setLoading(true);
}
return ;
}
```
**Pattern:** Hook at component level, setter in handler.
### Hooks in Classes
❌ **Wrong:**
```javascript
function BadCounter() {
const [count, setCount] = useState(0);
return
;
}
```
**Pattern:** Use function components for Hooks.
### Hooks in Callbacks
❌ **Wrong:**
```javascript
function Theme() {
const style = useMemo(() => {
const theme = useContext(ThemeContext);
return createStyle(theme);
}, []);
}
```
✅ **Right:**
```javascript
function Theme() {
const theme = useContext(ThemeContext);
const style = useMemo(() => createStyle(theme), [theme]);
}
```
**Pattern:** Call Hook at top level, reference in callback.
### Hooks After Early Returns
❌ **Wrong:**
```javascript
function User({ userId }) {
if (!userId) return null;
const user = useUser(userId);
return
{user.name}
;
}
```
✅ **Right:**
```javascript
function User({ userId }) {
const user = useUser(userId || null);
if (!userId) return null;
return
{user.name}
;
}
```
**Pattern:** Call all Hooks before any returns.
## Custom Hooks
Custom Hooks may call other Hooks because they execute during render phase:
```javascript
function useDebounce(value, delay) {
const [debounced, setDebounced] = useState(value);
useEffect(() => {
const timer = setTimeout(() => setDebounced(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debounced;
}
```
**Requirements:** Name starts with `use`; called from function component or another custom Hook; follows same Rules of Hooks.
## Quick Diagnostic
**ESLint error:** "React Hook cannot be called..."
1. Check location: Is Hook inside if/loop/try/handler/class?
2. Move Hook to top level of component/custom Hook
3. Keep conditional logic, move Hook call outside it
4. Use conditional rendering, not conditional Hooks
**Reference:** https://react.dev/reference/rules/rules-of-hooks