5月28日 02:51

What is the difference between useCallback and useMemo? When to use them?

Background

useCallback and useMemo are two performance optimization hooks provided by React. They look similar but have fundamental differences in purpose and return values.

Core Difference

Syntax Comparison

jsx
// useCallback: Returns the function itself const memoizedCallback = useCallback(() => { doSomething(a, b); }, [a, b]); // useMemo: Returns the result of function execution const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

Essential Differences

FeatureuseCallbackuseMemo
Return ValueFunction referenceAny value (computed result)
PurposeCache functionCache computed result
Similar toFunction memoizationValue memoization
Use CaseAvoid function recreationAvoid repeated computation

useCallback Details

Basic Usage

jsx
const memoizedCallback = useCallback( () => { doSomething(a, b); }, [a, b] );

Use Cases

1. Passing to Child Components to Avoid Unnecessary Renders

jsx
function Parent({ items }) { // ❌ Creates new function every render, causes Child to re-render const handleClick = () => { console.log("clicked"); }; // ✅ Use useCallback to cache function const handleClick = useCallback(() => { console.log("clicked"); }, []); return <Child onClick={handleClick} items={items} />; } // Use with React.memo const Child = React.memo(({ onClick, items }) => { console.log("Child render"); return <button onClick={onClick}>Click</button>; });

2. As useEffect Dependency

jsx
function UserProfile({ userId }) { // ❌ fetchUser is new reference every time, useEffect runs every time const fetchUser = () => { api.getUser(userId); }; // ✅ Use useCallback to stabilize function reference const fetchUser = useCallback(() => { api.getUser(userId); }, [userId]); useEffect(() => { fetchUser(); }, [fetchUser]); }

useMemo Details

Basic Usage

jsx
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

Use Cases

1. Avoid Repeated Computation

jsx
function ProductList({ products, filter }) { // ❌ Re-filters every render const filteredProducts = products.filter(p => p.name.includes(filter) ); // ✅ Only recomputes when products or filter changes const filteredProducts = useMemo(() => products.filter(p => p.name.includes(filter)), [products, filter] ); return <ul>{filteredProducts.map(p => <li key={p.id}>{p.name}</li>)}</ul>; }

2. Complex Computation Optimization

jsx
function DataTable({ data }) { // ✅ Large data sorting computed once const sortedData = useMemo(() => { console.log("Sorting..."); return [...data].sort((a, b) => a.score - b.score); }, [data]); // ✅ Complex data transformation const chartData = useMemo(() => { return data.reduce((acc, item) => { // Complex aggregation logic return acc; }, {}); }, [data]); return <Chart data={chartData} />; }

3. Reference Stability

jsx
function Parent({ items }) { // ❌ Creates new object every render, causes child re-render const style = { color: "red" }; // ✅ Keep object reference stable const style = useMemo(() => ({ color: "red" }), []); return <Child style={style} />; }

Best Practices

  1. Measure first, optimize later: Use React DevTools Profiler to find bottlenecks
  2. Simple computations do not need caching: Caching has overhead
  3. Use with React.memo: Ensure child components implement shouldComponentUpdate
  4. Declare dependencies correctly: Follow ESLint hints
标签:ReactReact Hook