Technology · React

React useMemo and useCallback

Optimize React performance by memoizing values and callbacks to prevent unnecessary re-renders.

TL;DR
  1. 01Use useMemo to cache expensive calculations between renders.
  2. 02Use useCallback to keep function references stable for optimization.
  3. 03Pass a dependency array to control when memoization resets.

useMemo Basics

  • Import useMemo from React at the top of your component file.
    import { useMemo } from "react";
  • Wrap an expensive calculation to cache its result between renders.
    const total = useMemo(() => {
      return items.reduce((sum, item) => sum + item.price, 0);
    }, [items]);
  • The hook returns the cached value, not the function itself.
  • Use a dependency array to reset the cache when inputs change.
  • Only use useMemo when the calculation is slow and frequent.

useCallback Basics

  • Import useCallback from React at the top of your component file.
    import { useCallback } from "react";
  • Memoize a callback function so its reference stays the same between renders.
    const handleClick = useCallback(() => {
      console.log("clicked");
    }, []);
  • Pass the memoized function to child components expecting stable references.
  • Use a dependency array that includes every value the callback reads.
  • The reference only changes when a dependency changes.

Dependency Arrays

  • An empty dependency array [] memoizes forever, never resetting.
    const value = useMemo(() => expensive(), []);
  • Include values that the calculation depends on in the array.
    const filtered = useMemo(() => filter(items, search), [items, search]);
  • The cache resets when any dependency changes, then recalculates fresh.
  • Forgetting a dependency can cause stale values and hard-to-find bugs.
  • Use ESLint rules to catch missing dependencies automatically.

When to Use Them

  • Use useMemo only for calculations that noticeably slow down rendering.
  • Use useCallback when passing functions to optimized child components.
  • Use React.memo() alongside useCallback for component memoization.
  • Avoid premature optimization — measure first with DevTools profiler.
  • Do not memoize simple values or callbacks — the overhead costs more.

Common Patterns

  • Memoize object literals passed as props to prevent child re-renders.
    const config = useMemo(() => ({
      timeout: 5000, retries: 3
    }), []);
  • Use useCallback with useEffect to avoid infinite loops.
    const fetch = useCallback(async () => {
      const res = await api.get();
      setData(res);
    }, []);
  • Combine with useMemo to memoize complex derived state.
  • Watch the React DevTools profiler to confirm memoization helps.
  • Consider using a state management library for complex memoization needs.

Tip: Use the React DevTools Profiler to measure actual performance gains before and after memoization, since it adds overhead.

Warning: Missing dependencies in the array causes stale closures and subtle bugs, so enable the ESLint rule to catch them automatically.

React Forms HandlingReact useReducer Hook