乐闻世界logo
搜索文章和话题

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

3月15日 00:43

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