useMemo 和 useCallback 是 React 面試高頻題。
這題不是在問你會不會背 API,而是在看你是否理解:
- React component re-render 時會重新執行 function body
- JavaScript 裡物件和函式會產生新的 reference
- memoization 有成本,不是加越多越好
useMemo、useCallback、React.memo要一起理解
useMemo 是什麼?
useMemo 用來記住「計算結果」。
const filteredProducts = useMemo(() => {
return products.filter((product) => product.category === selectedCategory);
}, [products, selectedCategory]);當 dependency 沒變時,React 會重用上一次的結果,不重新執行這段計算。
適合情境:
- 過濾大量資料
- 排序大量資料
- expensive calculation
- 產生傳給 child 的穩定 object / array
面試回答:
useMemo 是 memoize 計算結果。當 dependencies 沒變時,React 會重用上一次的 value,避免昂貴計算重複執行,或避免每次 render 都產生新的 object / array reference。
useCallback 是什麼?
useCallback 用來記住「函式 reference」。
const handleSelect = useCallback((id: string) => {
setSelectedId(id);
}, []);它可以理解成:
const handleSelect = useMemo(() => {
return (id: string) => {
setSelectedId(id);
};
}, []);適合情境:
- callback 傳給
React.memo包住的 child - callback 是
useEffectdependency - 第三方 library 需要穩定 function reference
useMemo / useCallback 差異
一句話:
useMemo記住值,useCallback記住函式。
| Hook | 記住什麼 | 常見用途 |
|---|---|---|
useMemo | 計算後的 value | 過濾、排序、衍生資料、穩定 object |
useCallback | function reference | 傳給 memo child、effect dependency |
React.memo 又是什麼?
React.memo 是 memoize component render result。
const ProductRow = memo(function ProductRow({ product, onSelect }) {
return <button onClick={() => onSelect(product.id)}>{product.name}</button>;
});如果 props 沒變,React 可以跳過這個 child 的 render。
但如果每次 parent render 都傳新的 function:
<ProductRow product={product} onSelect={(id) => setSelectedId(id)} />onSelect reference 每次都變,React.memo 就可能失去效果。
這時才可能需要 useCallback:
const handleSelect = useCallback((id: string) => {
setSelectedId(id);
}, []);
<ProductRow product={product} onSelect={handleSelect} />什麼時候不該用?
不要看到函式就加 useCallback,也不要看到計算就加 useMemo。
不太需要 memo 的例子:
const fullName = useMemo(() => {
return `${firstName} ${lastName}`;
}, [firstName, lastName]);這種計算太便宜,memo 的 dependency 比對和心智成本可能比計算本身還高。
不需要 useCallback 的例子:
function Button() {
const handleClick = () => {
console.log("click");
};
return <button onClick={handleClick}>Click</button>;
}如果沒有傳給 memo child,也沒有作為 effect dependency,通常不用特別包。
常見追問
useMemo 可以避免 component re-render 嗎?
不能。
useMemo 只能避免某段計算重跑,不能阻止 component 本身重新執行。
如果要避免 child 不必要 re-render,通常要搭配 React.memo 和穩定 props。
dependency array 寫錯會怎樣?
可能拿到舊資料,也可能造成 memo 失效。
如果漏 dependency,useMemo / useCallback 可能一直使用舊 closure。
useCallback 會讓函式不被建立嗎?
更準確地說,它讓 React 在 dependencies 沒變時回傳同一個 function reference。不要把它理解成完全沒有成本。
面試回答模板
useMemo 是記住計算結果,useCallback 是記住函式 reference。它們主要用在昂貴計算、穩定傳給 memo child 的 props,或避免 effect dependency 一直變。它們不是保證效能變好的魔法,因為 memoization 本身也有成本。我通常會先用 React DevTools Profiler 確認瓶頸,再針對昂貴計算或不必要 re-render 加 useMemo、useCallback 或 React.memo。