React useMemo 與 useCallback 效能優化完整教學:別再亂用了
我知道很多人一聽到「效能優化」就會想把 useMemo 和 useCallback 到處加。但我得先說一句實話:亂用這兩個 Hook 反而會讓你的 app 變慢。今天就來好好聊聊,什麼時候該用、什麼時候不該用。
useMemo 和 useCallback 到底做了什麼?
簡單來說,它們都是「記憶化」(memoization)的工具:
- useMemo:快取一個計算結果,只有當依賴改變時才重新計算
- useCallback:快取一個函式參照,只有當依賴改變時才回傳新的函式
// useMemo - 快取計算結果
const filteredItems = useMemo(() => {
return items.filter(item => item.name.includes(searchTerm));
}, [items, searchTerm]);
// useCallback - 快取函式參照
const handleClick = useCallback((id) => {
setSelectedId(id);
}, []);
看起來很美好對吧?但問題在於,每次使用這些 Hook 時,React 都要額外做依賴比較的工作。如果你 memo 的東西本身就很便宜,那這個「比較」的成本可能比「重新計算」還高。
什麼時候該用?
useMemo 的正確使用場景
第一種是真正昂貴的計算,例如對一個大陣列做排序或過濾:
function ProductList({ products, filters }) {
// 上千筆資料的多重過濾 + 排序,值得 memo
const filteredProducts = useMemo(() => {
return products
.filter(p => p.category === filters.category)
.filter(p => p.price >= filters.minPrice)
.filter(p => p.price <= filters.maxPrice)
.sort((a, b) => a.price - b.price);
}, [products, filters]);
return filteredProducts.map(p => );
}
第二種是建立一個物件或陣列作為子元件的 props,避免每次渲染都建立新的 reference:
const chartData = useMemo(() => ({
labels: data.map(d => d.date),
values: data.map(d => d.amount)
}), [data]);
useCallback 的正確使用場景
useCallback 最重要的使用場景是搭配 React.memo 的子元件:
const MemoizedChild = React.memo(({ onClick, label }) => {
console.log('Child rendered:', label);
return ;
});
function Parent() {
const [count, setCount] = useState(0);
// 沒有 useCallback,每次 Parent 渲染都會產生新函式
// 導致 MemoizedChild 的 memo 失效
const handleClick = useCallback(() => {
console.log('clicked');
}, []);
return (
Count: {count}
);
}
注意:如果子元件沒有用 React.memo 包裹,那 useCallback 是完全沒有意義的。這是最常見的錯誤。
什麼時候不該用?
以下情況請不要使用:
- 簡單的計算:加法、字串串接、基本的陣列 map
- 沒有搭配 React.memo 的 useCallback
- 依賴項每次都在變(等於每次都重新計算,memo 白做)
- 元件本身渲染就很快的情況
// 不要這樣做!這比不 memo 還慢
const fullName = useMemo(() => {
return firstName + ' ' + lastName;
}, [firstName, lastName]);
// 直接算就好
const fullName = firstName + ' ' + lastName;
用 React DevTools 量測效能
在決定要不要加 useMemo/useCallback 之前,先用 React DevTools 的 Profiler 看看你的元件到底花了多少時間渲染。開啟步驟:打開瀏覽器 DevTools → 切到 React DevTools 的 Profiler tab → 點 Record → 操作你的 app → 停止錄製。
如果一個元件的渲染時間不到 1ms,那真的沒必要 memo。你的時間花在真正的瓶頸上比較有價值。想了解更多 React 新功能,可以看看 React 19 新功能 的介紹。
React Compiler 的未來
好消息是,React 團隊正在開發 React Compiler,它會自動幫你做 memoization。未來你可能根本不需要手動寫 useMemo 和 useCallback。但在那之前,還是要理解這些概念,因為 Compiler 目前還在實驗階段。
如果你在用 Next.js,React Server Components 也是另一個大幅提升效能的方式,因為它直接在伺服器端渲染,根本不需要客戶端的 memoization。
重點整理
記住這個原則:先測量,再優化。useMemo 用在昂貴的計算,useCallback 搭配 React.memo 使用。不要過度使用,每個 memo 都有成本。如果你想深入學習 TypeScript 泛型,它能幫助你寫出更型別安全的自訂 Hook。
繼續閱讀
Next.js Partial Prerendering (PPR) 完整教學:靜態殼加動態串流大幅提升 LCP
Next.js 15 的 Partial Prerendering(PPR)是近年來前端效能優化最令人興奮的突破之一。它讓你在同一個頁面中,同時擁有靜態內容的極速首屏和動態內容的即時更新。本文深入解析 PPR 的運作原理,並帶你實作靜態殼加動態串流的頁面架構,讓你的 Core Web Vitals 分數大幅提升。
相關文章
你可能也喜歡
探索其他領域的精選好文