Difference between useMemo and useCallback?
The Core Difference
Both useMemo and useCallback are React hooks for memoization, but they memoize different things:
useMemo: Memoizes the result of a functionuseCallback: Memoizes the function itself
// useMemo - memoizes the VALUE
const expensiveValue = useMemo(() => {
return computeExpensiveValue(a, b);
}, [a, b]);
// useCallback - memoizes the FUNCTION
const memoizedCallback = useCallback(() => {
doSomething(a, b);
}, [a, b]);
// These are equivalent:
useCallback(fn, deps) === useMemo(() => fn, deps)
When to Use useMemo
Use useMemo when you have expensive calculations that you don’t want to run on every render.
Example: Filtering Large Lists
function ProductList({ products, searchTerm }) {
// ❌ Without useMemo - filters on EVERY render
const filteredProducts = products.filter(p =>
p.name.toLowerCase().includes(searchTerm.toLowerCase())
);
// ✅ With useMemo - only filters when dependencies change
const filteredProducts = useMemo(() => {
return products.filter(p =>
p.name.toLowerCase().includes(searchTerm.toLowerCase())
);
}, [products, searchTerm]);
return (
<ul>
{filteredProducts.map(p => <li key={p.id}>{p.name}</li>)}
</ul>
);
}
When to Use useCallback
Use useCallback when passing callbacks to optimized child components that rely on reference equality.
Example: Preventing Child Re-renders
function Parent() {
const [count, setCount] = useState(0);
const [other, setOther] = useState(0);
// ❌ Without useCallback - new function every render
const handleClick = () => {
setCount(c => c + 1);
};
// ✅ With useCallback - same function reference
const handleClick = useCallback(() => {
setCount(c => c + 1);
}, []); // No dependencies - function never changes
return (
<>
<button onClick={() => setOther(o => o + 1)}>
Other: {other}
</button>
{/* Child won't re-render when 'other' changes */}
<ExpensiveChild onClick={handleClick} />
</>
);
}
const ExpensiveChild = React.memo(({ onClick }) => {
console.log('ExpensiveChild rendered');
return <button onClick={onClick}>Increment</button>;
});
Common Pitfalls
1. Premature Optimization
// ❌ Unnecessary - simple calculation
const doubled = useMemo(() => count * 2, [count]);
// ✅ Just do it directly
const doubled = count * 2;
2. Missing Dependencies
// ❌ Stale closure - missing dependency
const handleSubmit = useCallback(() => {
api.submit(formData); // formData not in deps!
}, []);
// ✅ Include all dependencies
const handleSubmit = useCallback(() => {
api.submit(formData);
}, [formData]);
3. Over-memoization
// ❌ Overhead > Benefit
const Component = () => {
const a = useMemo(() => 1, []);
const b = useMemo(() => 2, []);
const sum = useMemo(() => a + b, [a, b]);
return <div>{sum}</div>;
};
// ✅ Simple is better
const Component = () => {
return <div>{3}</div>;
};
Performance Measurement
Always measure before optimizing:
import { Profiler } from 'react';
function App() {
const onRenderCallback = (
id,
phase,
actualDuration,
baseDuration,
startTime,
commitTime
) => {
console.log(`${id} (${phase}) took ${actualDuration}ms`);
};
return (
<Profiler id="ProductList" onRender={onRenderCallback}>
<ProductList />
</Profiler>
);
}
Decision Tree
Need to memoize something?
│
├─ Is it a function?
│ │
│ ├─ Yes → useCallback
│ │
│ └─ Is it passed to a memoized child?
│ │
│ ├─ Yes → useCallback
│ └─ No → Probably don't need it
│
└─ Is it a value?
│
├─ Is the calculation expensive?
│ │
│ ├─ Yes → useMemo
│ └─ No → Don't memoize
│
└─ Is it used as a dependency?
│
├─ Yes → useMemo (for referential equality)
└─ No → Probably don't need it tips_and_updates
AI 深度解析
需要更详细的解释或代码示例?让 AI 助教为你深度分析。