技術 5 分鐘閱讀
React 效能優化實戰:memo、useMemo、useCallback 完整解析
前言
根據我的經驗,React 專案到了一定規模後,效能問題就會慢慢浮現。最近幫一個客戶優化後台系統,把列表頁的渲染時間從 800ms 降到 200ms。這篇整理一下用到的技巧。
先理解 React 的重新渲染機制
React 元件會在以下情況重新渲染:
- state 改變
- props 改變
- 父元件重新渲染
第三點最容易被忽略,也是效能問題的主要來源。
React.memo:避免不必要的重新渲染
interface ProductCardProps {
name: string;
price: number;
}
const ProductCard = React.memo(function ProductCard({ name, price }: ProductCardProps) {
console.log('ProductCard rendered');
return (
<div className="rounded-lg border p-4">
<h3>{name}</h3>
<p>NT$ {price}</p>
</div>
);
});
用 React.memo 包裝後,只有當 props 真正改變時才會重新渲染。
何時使用?
- 列表中的子元件(如商品卡片、表格行)
- 渲染成本高的元件(含複雜計算或大量 DOM)
- 父元件經常更新,但子元件的 props 不常變化
useMemo:快取計算結果
function OrderSummary({ items }: { items: OrderItem[] }) {
const totalPrice = useMemo(() => {
return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}, [items]);
const sortedItems = useMemo(() => {
return [...items].sort((a, b) => b.price - a.price);
}, [items]);
return (
<div>
<p>總金額:NT$ {totalPrice}</p>
{sortedItems.map(item => (
<div key={item.id}>{item.name}</div>
))}
</div>
);
}
何時使用?
- 計算量大的運算(排序、篩選大量資料)
- 需要保持物件參考穩定(傳給
memo元件的 props)
useCallback:快取函式參考
function ProductList() {
const [products, setProducts] = useState<Product[]>([]);
const [query, setQuery] = useState('');
const handleDelete = useCallback((id: number) => {
setProducts(prev => prev.filter(p => p.id !== id));
}, []);
const handleSearch = useCallback((searchQuery: string) => {
setQuery(searchQuery);
}, []);
return (
<div>
<SearchBar onSearch={handleSearch} />
{products.map(product => (
<ProductCard
key={product.id}
product={product}
onDelete={handleDelete}
/>
))}
</div>
);
}
何時使用?
- 傳給
memo元件的 callback function - 作為其他 Hook 的 dependency
效能分析工具
React DevTools Profiler
import { Profiler } from 'react';
function onRender(id, phase, actualDuration) {
console.log(`${id} ${phase}: ${actualDuration}ms`);
}
<Profiler id="ProductList" onRender={onRender}>
<ProductList />
</Profiler>
常見誤區
不要過度優化
// ❌ 不需要 - 簡單計算不用 useMemo
const fullName = useMemo(() => `${firstName} ${lastName}`, [firstName, lastName]);
// ✅ 直接計算
const fullName = `${firstName} ${lastName}`;
根據我的經驗,大部分效能問題都來自不必要的重新渲染和大量 DOM 操作。先用 Profiler 找到瓶頸,再針對性地優化,效果最好。
延伸閱讀
如果你需要類似的系統,歡迎聯繫討論。
#React #效能優化