React Hooks Deep Dive
Master React hooks - useState, useEffect, useRef, useMemo, useCallback, useContext, and custom hooks for reusable stateful logic.
skill: React Hooks Deep Dive category: Web Development tags: [React, Hooks, useState, useEffect, Custom Hooks] readingTime: 7
Hooks eliminated class components for good reason - they composable, testable, and co-locate logic with the component that uses it. The most common hook bugs come from wrong useEffect dependency arrays. Never lie to the exhaustive-deps linter rule - if it says add a dep, add it; if adding it causes an infinite loop, restructure the code.
Workflow
- Use useState for local component stateuseState holds values that, when changed, cause the component to re-render. Use it for UI state local to a single component.
- Master useEffect dependency arraysWrong dependency arrays are the source of most React bugs - missing deps cause stale closures; extra deps cause infinite loops.
- Optimize with useMemo and useCallbackuseMemo caches computed values; useCallback caches functions. Both prevent child re-renders when the parent renders with the same inputs.
- Build custom hooks for reusable logicCustom hooks extract stateful logic from components - making it reusable, testable, and composable across the app.
- Manage complex state with useReduceruseReducer is better than multiple useStates when state transitions are complex or related - makes state machines explicit.
Output Format
React hooks: useState functional updates, useEffect cleanup, useMemo/useCallback for optimization, custom hooks for reuse, useReducer for complex state.
// Custom hook pattern
function useXyz(param) {
const [state, setState] = useState(initial);
useEffect(() => { /* side effect */; return () => { /* cleanup */ }; }, [param]);
const action = useCallback(() => { setState(...); }, []);
return { state, action };
}Examples
Input
Output
Edge Cases & Gotchas
- useEffect cleanup race condition - Async fetch that sets state after component unmounts causes memory leak warning. Use a cancelled flag or AbortController in the cleanup.
- Object/array as dependency - [{ id: 1 }] as a dep creates a new object every render - useEffect runs every time. Stabilize with useMemo or stringify the dep.
- useRef for latest value in callbacks - Event handlers capture a stale closure of state. Store the latest value in a ref: const latestState = useRef(state); latestState.current = state;
- React 19 compiler - React 19's compiler automatically memoizes components and hooks - useMemo/useCallback may become unnecessary. Don't add them preemptively; profile first.
When to Break These Rules
- Use Zustand or Jotai instead of useState + Context for global state - the Context + useReducer pattern causes over-rendering at scale.
- Use React Query / TanStack Query instead of custom useFetch hooks - it handles caching, deduplication, background refetch, and optimistic updates.
Reference Files
https://react.dev/reference/react/hookshttps://usehooks-ts.com/