Technology · React
React useMemo and useCallback
Optimize React performance by memoizing values and callbacks to prevent unnecessary re-renders.
TL;DR
- 01Use useMemo to cache expensive calculations between renders.
- 02Use useCallback to keep function references stable for optimization.
- 03Pass a dependency array to control when memoization resets.
useMemo Basics
- Import
useMemofrom 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
useMemowhen the calculation is slow and frequent.
useCallback Basics
- Import
useCallbackfrom 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
useMemoonly for calculations that noticeably slow down rendering. - Use
useCallbackwhen passing functions to optimized child components. - Use
React.memo()alongsideuseCallbackfor 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
useCallbackwithuseEffectto avoid infinite loops.const fetch = useCallback(async () => { const res = await api.get(); setData(res); }, []); - Combine with
useMemoto 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.