--- 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
{count}
; } ``` ✅ **Right:** ```javascript function Counter() { const [count, setCount] = useState(0); return
{count}
; } ``` **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