Web Development7 min read

React Hooks Deep Dive

Master React hooks - useState, useEffect, useRef, useMemo, useCallback, useContext, and custom hooks for reusable stateful logic.

ReactHooksuseStateuseEffectCustom Hooks

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

  1. 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.
  2. Master useEffect dependency arraysWrong dependency arrays are the source of most React bugs - missing deps cause stale closures; extra deps cause infinite loops.
  3. Optimize with useMemo and useCallbackuseMemo caches computed values; useCallback caches functions. Both prevent child re-renders when the parent renders with the same inputs.
  4. Build custom hooks for reusable logicCustom hooks extract stateful logic from components - making it reusable, testable, and composable across the app.
  5. 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/hooks
  • https://usehooks-ts.com/

More skills in this library

Browse all technical skills - Mobile, AI/ML, Web, Backend, Design, and Product.