ReactCheatsheet

React Hooks Cheatsheet📋

Every built-in React hook in one place, including the React 19 additions. Each entry has the signature, a working example, and practical tips. Bookmark this and stop Googling.

Quick Reference

NameSyntaxDescription
useStateconst [state, setState] = useState(initial)Reactive state variable. Triggers re-render on change.
useEffectuseEffect(() => { ... return cleanup }, [deps])Side effects after render. Cleanup runs before next effect and on unmount.
useRefconst ref = useRef(initialValue)Mutable value that persists across renders without triggering re-renders.
useMemoconst value = useMemo(() => compute(a, b), [a, b])Caches a computed value. Only recalculates when dependencies change.
useCallbackconst fn = useCallback(() => { ... }, [deps])Caches a function reference. Useful for stable props to memoized children.
useReducerconst [state, dispatch] = useReducer(reducer, init)State machine pattern. Better than useState for complex state logic.
useContextconst value = useContext(MyContext)Reads the nearest context provider's value.
useTransitionconst [isPending, startTransition] = useTransition()Marks state updates as non-blocking. Keeps the UI responsive.
useIdconst id = useId()Generates a stable unique ID safe for SSR hydration.
use (React 19)const data = use(promise)Reads a Promise or Context during render. Can be called conditionally.

State Hooks

useState

useState<T>(initialValue: T | (() => T))

Returns a state variable and its setter. The setter accepts either a new value or a function that receives the previous state.

tsx

Tips

  • Use functional updates when the new state depends on the old state — avoids stale closure bugs
  • Lazy initializers run only on mount, not on every render
  • setState doesn't merge objects like class components did — spread manually: setState(prev => ({ ...prev, name: 'new' }))

useReducer

useReducer(reducer, initialState, init?)

State management via action dispatch. Prefer over useState when state has multiple sub-values, next state depends on previous, or state transitions follow clear rules.

tsx

Tips

  • Type your actions as a discriminated union — TypeScript will check you handle all cases
  • The third argument (init) is for lazy initialization from a prop value
  • Combine with useContext for lightweight global state (no external library needed)

Effect Hooks

useEffect

useEffect(setup, dependencies?)

Synchronizes your component with an external system (APIs, subscriptions, DOM manipulation). The cleanup function runs before the next effect and on unmount.

tsx

Tips

  • The dependency array controls when the effect re-runs. Empty array = mount only. No array = every render.
  • Always cancel async operations in cleanup to prevent setting state on unmounted components
  • If you don't need to synchronize with something external, you probably don't need an effect — derive state during render instead

useLayoutEffect

useLayoutEffect(setup, dependencies?)

Same API as useEffect, but fires synchronously after DOM mutations and before the browser paints. Use for DOM measurements or mutations that must be visible immediately.

tsx

Tips

  • Only use this when useEffect causes a visible flicker (tooltip positioning, scroll restoration)
  • Blocks painting — heavy work here will freeze the UI
  • Falls back to useEffect during SSR

Ref Hooks

useRef

useRef<T>(initialValue: T)

Returns a mutable ref object whose .current property persists across renders. Changing it does not trigger a re-render. Two main uses: DOM element access and storing mutable values.

tsx

Tips

  • For DOM refs, initialize with null and type as useRef<HTMLElement>(null)
  • Use refs to store interval/timeout IDs, previous prop values, or any value that shouldn't trigger re-renders
  • Don't read or write .current during render — only in effects and event handlers

useImperativeHandle

useImperativeHandle(ref, createHandle, dependencies?)

Customizes the instance value exposed to parent components via ref. Use sparingly — prefer passing data through props.

tsx

Tips

  • React 19 lets you pass ref as a regular prop — no more forwardRef wrapper needed
  • Only expose the minimum API the parent needs
  • This is an escape hatch, not a communication pattern — prefer props and callbacks

Performance Hooks

useMemo

useMemo(() => computeValue(a, b), [a, b])

Memoizes a computed value. Only recalculates when dependencies change. Not a guarantee — React may drop the cache under memory pressure.

tsx

Tips

  • Don't memoize everything — only computations that are actually expensive or produce objects/arrays passed to memoized children
  • The React Compiler (experimental) may make manual useMemo unnecessary in the future
  • If the computation is trivial, useMemo adds overhead for no benefit

useCallback

useCallback(fn, dependencies)

Caches a function so its reference stays the same between renders. useCallback(fn, deps) is equivalent to useMemo(() => fn, deps).

tsx

Tips

  • Only useful when the function is passed to a component wrapped in React.memo or used as a useEffect dependency
  • If the child isn't memoized, useCallback does nothing — the child re-renders regardless
  • Prefer useCallback over inline functions in dependency arrays of effects to prevent infinite loops

useTransition

const [isPending, startTransition] = useTransition()

Marks a state update as non-blocking. React will interrupt the transition to handle urgent updates (like typing), keeping the UI responsive.

tsx

Tips

  • Use for expensive renders triggered by user input (filtering, tab switching, navigation)
  • isPending lets you show a loading indicator while the transition runs
  • Only works with state updates — you can't wrap async operations directly

React 19 Hooks

use

const value = use(resource)

Reads a Promise or Context during render. Unlike other hooks, use() can be called inside conditionals and loops. When reading a Promise, it integrates with Suspense for loading states.

tsx

Tips

  • Don't create the Promise inside the component — pass it as a prop or create it in a parent/server component
  • Pair with <Suspense> for loading boundaries and <ErrorBoundary> for error handling
  • use(Context) is interchangeable with useContext(Context) but can be called conditionally

useActionState

const [state, formAction, isPending] = useActionState(action, initialState)

Manages form state based on the result of a form action. Works with both client and server actions. The form works even before JavaScript loads (progressive enhancement).

tsx

Tips

  • The action receives the previous state as its first argument and FormData as its second
  • isPending is true while the action is running — use it to disable submit buttons
  • Works with Next.js server actions for full-stack form handling

useOptimistic

const [optimistic, addOptimistic] = useOptimistic(state, updateFn)

Shows an optimistic state while an async action is in progress. Automatically reverts if the action fails. Makes the UI feel instant.

tsx

Tips

  • The optimistic state reverts when the parent component updates the real state (e.g., after revalidation)
  • The update function receives current optimistic state and the value you pass to addOptimistic
  • Combine with useTransition or server actions for the best user experience

Common Patterns

Custom hook: useLocalStorage

tsx

Syncs state to localStorage. The lazy initializer reads the stored value on mount, and the effect persists it on change. This is the most common custom hook pattern.

Context + Reducer for global state

tsx

Separate state and dispatch into two contexts so components that only dispatch don't re-render when state changes. Lightweight alternative to Redux or Zustand for medium complexity state.

Watch Out For

useEffect fires after paint, not before — if you see a flicker, you need useLayoutEffect

Switch to useLayoutEffect for DOM measurements or mutations that must be visible immediately (tooltip positioning, scroll restoration). But don't overuse it — it blocks painting.

Stale closures: an effect or callback captures an old variable value because the dependency array is wrong

Always include every value from the component scope that the effect reads. Use the eslint-plugin-react-hooks exhaustive-deps rule — it catches this automatically.

Infinite re-render loop: setting state inside useEffect without proper dependencies

Never set state unconditionally inside an effect. Make sure the dependency array doesn't include values that the effect itself changes, or use a ref to break the cycle.

useMemo/useCallback don't prevent re-renders by themselves — the child must be wrapped in React.memo

Memoizing a prop value is useless if the child component re-renders regardless. Only reach for useMemo/useCallback when the child is wrapped in React.memo or the value is an effect dependency.

useRef changes don't trigger re-renders — if you need reactivity, use state

useRef is for values you want to persist without causing renders (DOM refs, interval IDs, previous values). If the UI should update when the value changes, use useState.

Master React with Stanza

Go beyond the cheatsheet with hands-on lessons and challenges.

Dive Deeper