2.7 — Useful Hooks in React: Quick Revision
Compact cheat sheet. Print-friendly. One glance per concept.
How to use this material:
- Use this for quick refreshers before interviews or coding sessions
- If a concept feels unfamiliar, revisit the full sub-topic file
- Focus on the code patterns — they're the most practical
- Test yourself: cover the right column and try to recall from the left
2.7.a — Understanding React Hooks
What Are Hooks?
| Concept | Key Point |
|---|
| Definition | Functions starting with use that access React internals from function components |
| Three problems solved | 1) Logic reuse (replaced HOCs/render props) 2) Code organization (group by concern) 3) No more this confusion |
| Mental model shift | Class: "when does this run?" → Hooks: "what am I synchronizing with?" |
| Closure model | Each render has its own snapshot of state, props, handlers, effects |
| Internal mechanism | Linked list indexed by call order (why rules exist) |
| Custom hooks | Compose built-in hooks; each consumer gets independent state copy |
Built-in Hooks Quick Reference
useState(init) → [value, setter] — UI-visible state
useEffect(fn, deps) → void — Side effects after paint
useContext(Context) → value — Read from Provider tree
useRef(init) → { current } — DOM refs + mutable values
useCallback(fn, deps) → cachedFn — Stable function reference
useMemo(() => val, deps) → cachedValue — Cached computation
useReducer(reducer, init) → [state, dispatch] — Complex state transitions
useId() → ":r0:" string — SSR-safe unique ID
useTransition() → [isPending, startFn] — Non-urgent updates
useDeferredValue(value) → deferredValue — Show stale while computing
useLayoutEffect(fn, deps) → void — Before paint (DOM measure)
2.7.b — Rules of Hooks
The Two Rules
| Rule | Meaning | Why |
|---|
| Top level only | No hooks in if/for/nested functions/after early returns | React uses call ORDER to match hooks to stored state |
| React functions only | Only in components or custom hooks (starting with use) | Hooks attach to Fiber nodes — no component = no Fiber |
Common Violations & Fixes
if (x) useEffect(fn); useEffect(() => { if (x) { fn(); } }, [x]);
if (!data) return null; const [s, setS] = useState(0);
const [s, setS] = useState(0); if (!data) return null;
items.map(i => { items.map(i => <Item key={i.id} />)
const [v, set] = useState(0); function Item() { const [v, set] = useState(0); }
});
ESLint Plugin
{ "react-hooks/rules-of-hooks": "error", "react-hooks/exhaustive-deps": "warn" }
Exhaustive Deps Fixes
| Warning | Fix |
|---|
| Missing primitive dep | Add to array |
| Missing function dep | Move inside effect OR wrap in useCallback |
| Missing object dep | Wrap in useMemo OR move inside effect |
| Want latest without re-run | Use useRef |
| Intentional fire-once | Suppress with // eslint-disable-next-line + comment |
2.7.c — Commonly Used Hooks
useState Patterns
const [v, set] = useState(init);
const [v, set] = useState(() => expensive);
set(5);
set(prev => prev + 1);
set(prev => ({ ...prev, age: 26 }));
| Pattern | When |
|---|
Direct value set(5) | New state independent of previous |
Updater set(prev => ...) | New state depends on previous |
Lazy init useState(() => ...) | Initial value is expensive to compute |
| Object spread | Updating one field of object state |
Key reset <Comp key={id} /> | Reset ALL state when switching items |
useEffect Patterns
useEffect(() => { ... });
useEffect(() => { ... }, []);
useEffect(() => { ... }, [dep]);
useEffect(() => {
const sub = subscribe();
return () => sub.unsubscribe();
}, []);
| Pattern | Code |
|---|
| Fetch + abort | const ctrl = new AbortController(); fetch(url, {signal}) + cleanup ctrl.abort() |
| Debounced search | setTimeout in effect + cleanup clearTimeout |
| Subscription | subscribe in effect + unsubscribe in cleanup |
| Event listener | addEventListener + cleanup removeEventListener |
useContext Pattern
const Ctx = createContext(null);
function Provider({ children }) {
const value = useMemo(() => ({ ... }), [deps]);
return <Ctx.Provider value={value}>{children}</Ctx.Provider>;
}
function useCtx() {
const ctx = useContext(Ctx);
if (!ctx) throw new Error('Must be inside Provider');
return ctx;
}
useRef Quick Reference
| Use Case | Code |
|---|
| DOM reference | const ref = useRef(null); <div ref={ref} /> |
| Timer ID | const timer = useRef(null); timer.current = setInterval(...) |
| Previous value | const prev = useRef(); useEffect(() => { prev.current = value; }); |
| Latest callback | const cbRef = useRef(cb); cbRef.current = cb; |
| Render count | const count = useRef(0); count.current++; |
useCallback vs useMemo Decision
Need it?
├── Passing fn to React.memo child → useCallback ✅
├── Used as useEffect dependency → useCallback ✅
├── Expensive computation → useMemo ✅
├── Object/array to memo'd child → useMemo ✅
├── Context provider value → useMemo ✅
└── Everything else → Neither ❌ (no benefit)
useReducer vs useState
| useState | useReducer |
|---|
| 1-2 simple values | 3+ related values |
| Direct replacements | Named action types |
| Simple logic | Complex transitions |
| Test in component | Test reducer as pure function |
Hook Return Values
useState → [value, setter] (array)
useReducer → [state, dispatch] (array)
useContext → value (direct)
useRef → { current: value } (object)
useMemo → cached value (direct)
useCallback → cached function (direct)
useEffect → void (nothing)
useId → ":rN:" string (direct)
useTransition→ [isPending, startFn] (array)
Core Formulas
Hooks model: UI = f(state) — each render is a snapshot
Closure rule: setTimeout captures render-time values, not latest
State update: setX(prev => ...) chains correctly; setX(val) doesn't
Effect timing: render → paint → effect (non-blocking)
Layout timing: render → layoutEffect → paint (blocking)
Ref rule: .current mutations don't trigger re-render
Context rule: Provider value change → ALL useContext consumers re-render
Memo rule: Don't memoize until you measure a problem
When in doubt: useState for visible data, useRef for invisible data, useEffect for external sync, useContext for tree data.