🎤 Interview: "Functional updates are critical when the next state depends on the previous state, especially in async callbacks or event handlers. Lazy initialization is useful for reading from localStorage or performing expensive initial calculations."
useEffect
useEffect — Side Effects & Cleanup
Runs after render. Handles subscriptions, timers, network requests. Always cleanup!
⏱️ Timer: 0s (cleanup on unmount)
📏 Window Width: 0px (listener cleanup)
// Runs AFTER every render (no deps)useEffect(() => { /* side effect */ });
// Runs ONCE on mount (empty deps)useEffect(() => {
const timer = setInterval(...);
return () => clearInterval(timer); // cleanup
}, []);
// Runs when [id] changesuseEffect(() => { fetchUser(id); }, [id]);
🎤 Interview: "The deps array tells React when to re-run the effect. Empty deps = run once on mount. Missing deps = runs every render (usually a bug). Cleanup functions prevent memory leaks from subscriptions or timers."
useContext
useContext — Consume Context Without Prop Drilling
Access context value from nearest provider. Re-renders when context changes.
🎨 Current Theme: dark
Theme: dark
// 1. Create contextconst ThemeContext = createContext({});
// 2. Provide value
<ThemeContext.Provider value={{theme, setTheme}}>
<ChildComponent />
</ThemeContext.Provider>
// 3. Consume in child (no props!)const { theme, setTheme } = useContext(ThemeContext);
🎤 Interview: "useContext solves prop drilling. But be careful: every consumer re-renders when context changes. For performance, split contexts or use useMemo to stabilize the provider value."
useMemo
useMemo — Memoize Expensive Computations
Only re-runs when dependencies change. Skips re-computation on unrelated renders.
⚙️ Sorted: [1, 2, 5, 8, 9]
// ❌ Bad: sorts on EVERY renderconst sorted = [...nums].sort();
// ✅ Good: only sorts when [nums] changesconst sorted = useMemo(
() => [...nums].sort((a, b) => a - b),
[nums]
);
🎤 Interview: "useMemo caches a computed value. Use for expensive calculations like filtering large arrays. Don't over-memoize — the memo itself has overhead."
useCallback
useCallback — Stable Function References
Prevents child re-renders when passing callbacks as props (use with React.memo).
🔢 Callback clicked: 0 times
// ❌ Without: new function → child re-rendersconst handleClick = () => doSomething();
// ✅ With: same ref → child skips re-renderconst handleClick = useCallback(
() => doSomething(id),
[id]
);
useRef
useRef — DOM Access + Persistent Values
Mutable ref that persists across renders WITHOUT triggering re-render when changed.
// DOM refconst inputRef = useRef(null);
<input ref={inputRef} />
inputRef.current.focus();
// Persistent value (doesn't cause re-render)const renderCount = useRef(0);
renderCount.current++;
🎤 Interview: "useRef has two uses: (1) accessing DOM nodes, and (2) storing mutable values that persist across renders without causing re-renders. Unlike state, changing ref.current doesn't trigger a render."
useReducer
useReducer — Complex State Logic
Alternative to useState for complex state with multiple sub-values or transitions.
🎤 Interview: "useReducer is better than useState when: (1) state has multiple sub-values, (2) next state depends on previous, or (3) update logic is complex. It's Redux in miniature."
useLayoutEffect
useLayoutEffect vs useEffect
Fires synchronously BEFORE browser paint. Use for DOM measurements to avoid flicker.
📐 Measured Height: 0px
This height was measured in useLayoutEffect before the browser painted. With useEffect, you'd see a flicker as the height updates.
// useEffect: async, after paint (typical)useEffect(() => { /* DOM is painted */ });
// useLayoutEffect: sync, BEFORE paintuseLayoutEffect(() => {
const height = ref.current.offsetHeight;
setHeight(height); // no flicker
});
🎤 Interview: "99% of the time use useEffect. Use useLayoutEffect only when you need to measure DOM nodes or mutate the DOM before paint to avoid visual flicker."
useId
useId — SSR-Safe Unique IDs
Generates unique IDs that match between server and client (hydration-safe).
// ❌ Bad in SSR: Math.random() causes hydration mismatchconst id = "field-" + Math.random();
// ✅ Good: SSR-safe unique IDconst id = useId();
<label htmlFor={id}>Name</label>
<input id={id} />
🎤 Interview: "useId solves the hydration mismatch problem when generating unique IDs for accessibility attributes. The server and client generate the same ID, preventing React warnings."
🎤 Interview: "useDeferredValue keeps the UI responsive by deferring expensive computations. The input stays snappy while the heavy filter catches up. It's automatic concurrent rendering."
useTransition
useTransition — Mark State Updates as Non-Urgent
Wraps state updates in startTransition to keep UI responsive during expensive updates.
🎤 Interview: "useTransition lets you mark certain state updates as non-urgent. React keeps the UI responsive by prioritizing urgent updates (typing) over non-urgent ones (rendering 10k items). isPending tells you when the transition is active."
useImperativeHandle
useImperativeHandle — Customize Ref Exposure
Customize the instance value that gets exposed to parent when using forwardRef.
🎤 Interview: "useImperativeHandle customizes the ref value exposed to parents. Instead of exposing the raw DOM node, you expose a custom API (focus, shake). Rarely needed, but useful for reusable components."
Custom Hooks
Custom Hooks — Reusable Logic Patterns
Extract and reuse stateful logic. Must start with "use" to follow Rules of Hooks.
🎤 Interview: "Custom hooks let you extract component logic into reusable functions. Common patterns: useLocalStorage, useFetch, useDebounce, useMediaQuery. They must start with 'use' and can call other hooks."