React 性能優化:React.memo、useMemo、useCallback
React.memo、useMemo、useCallback 這三個是 React 性能優化一定會被提到的,今天就來比較一下他們的差異。

一、基本概念與本質差異
- React.memo:組件層級(Component-level)
是 HOC,擋在組件外部,決定「這個組件要不要 re-render」。 - useMemo:數值層級(Value-level)
- useCallback:函式引用層級(Function reference-level)
useMemo/useCallback 都是 Hook,作用在組件內部,不會阻止 render,只是在 render 發生時降低成本(避免重算或 reference 改變)。
二、它們解決了什麼問題?
React.memo —— 控制「要不要 render」
- 組件級優化
- React.memo 會對 props 做 shallow comparison,props reference 沒變 → 跳過 render
- 行為類似於 class component 的
PureComponent
const UserCard = React.memo(function UserCard({ user }) {
// 只有當 user prop 發生變化時,這裡才會執行
return <div>{user.name}</div>;
});
⚠️ 注意:它是 shallow comparison,不是做深度比對,所以object / array / function 只要 reference 變了,就視為「變了」
適合使用場境
- 子組件 render 成本高(DOM 多、邏輯複雜)
- 父組件 re-render 很頻繁
- 子組件的 props 大多數時間不變
不適合使用場境
- 組件很小、render 很輕,React.memo 本身有 props comparison 成本,反而會是負優化
- props 幾乎每次都會變(memo 很難命中)
useMemo —— 控制「要不要重新計算」
- 記憶「計算結果」
- 避免每次 Render 都重跑耗時的複雜運算(如大量的 Array filter/sort)。
// 只有當 data 改變時,才會重新執行 expensiveCalc
const total = useMemo(() => expensiveCalc(data), [data]);適合使用場境
- 計算成本明顯(資料量大、邏輯複雜)
- render 次數多,但 input(dependency)很少改變
- 計算結果會被多次使用
不適合使用場境
- 輕量計算(例如簡單 map、加減)
- 只為了「看起來比較專業」
useCallback —— 控制「Function Reference 是否穩定」
- 記憶 function 本身
- 本質等同於
useMemo(() => fn, deps)。
// 確保 handleClick 在多次 render 之間是同一個 reference
const handleClick = useCallback(() => {
console.log('Clicked');
}, []);
// 這樣傳給 Child 時,才不會因為 handleClick 變了而導致 Child 無謂重繪
<Child onClick={handleClick} />通常是為了配合 React.memo 使用,
避免因為 function reference 改變,導致 memo 失效。
適合使用場境
- 傳遞 function 給被
React.memo包住的子組件 - function 作為
useEffect / useMemo的 dependency - function 建立成本高,且 dependency 穩定
不適合使用場境
- function 只在本組件使用
- 沒有 memo 子組件,也沒 dependency 問題
三、優化決策流程
效能優化本身也有成本,一個重點是,避免過早優化(Premature Optimization),更不要為了優化而無差別套用 React.memo。
Step 1:先量測,再動手
- 使用 React Developer Tools Profiler
- 或 Chrome DevTools Performance
- 重點觀察:
- 哪些組件 re-render 很頻繁
- 哪些組件 render 本身很慢
- 是否存在明顯「不必要的 re-render」
Step 2:確認瓶頸在哪裡
場景 A:render 次數多,但每次 render 都在做重計算
例如:
- 幾千筆資料的
filter / sort / reduce - render 觸發頻繁,但 input 幾乎不變
👉 使用 useMemo 記住計算結果,避免重算。
場景 B:子組件 render 成本高,且 props 多數時間不變
例如:
- DOM 結構複雜
- 父組件狀態頻繁變動,導致子組件有不必要的 re-render 影響效能或體驗
👉 使用 React.memo,在組件層級擋掉不必要的 render。
場景 C:已使用 React.memo,但仍然頻繁 re-render
這通常不是 memo 沒用,而是 props reference 不穩定。
請檢查子組件 props 是否包含:
- function
- 若 function 每次 render 都重新建立
- 且會傳給被 memo 的子組件
👉 父組件可考慮使用useCallback穩定 reference
- object / array
- 若每次 render 都新建
- 且實際造成 memo 失效
👉 視情況使用useMemo穩定 reference
