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.

Tips

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

Warnings

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

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.

FAQ