useMemo / useCallback 与性能
约 356 字大约 1 分钟
布欧-Lewyon
2026-05-15
useMemo
缓存计算结果,避免每次渲染都重新计算。
import { useMemo, useState } from 'react'
function ExpensiveList({ items, filter }: { items: Item[]; filter: string }) {
// 只有 items 或 filter 变化时才重新计算
const filteredItems = useMemo(() => {
console.log('Filtering...')
return items.filter(item =>
item.name.toLowerCase().includes(filter.toLowerCase())
)
}, [items, filter])
return (
<ul>
{filteredItems.map(item => <li key={item.id}>{item.name}</li>)}
</ul>
)
}useCallback
缓存函数引用,避免子组件因函数引用变化而重渲染。
import { useCallback, useState, memo } from 'react'
// 子组件只有 Props 变化时才重渲染
const ChildButton = memo(({ onClick, label }: {
onClick: () => void; label: string
}) => {
console.log(`Rendered: ${label}`)
return <button onClick={onClick}>{label}</button>
})
function Parent() {
const [count, setCount] = useState(0)
const [other, setOther] = useState(0)
// ✅ 函数引用稳定,ChildButton 不会不必要重渲染
const increment = useCallback(() => {
setCount(c => c + 1)
}, [])
return (
<div>
<p>Count: {count}</p>
<ChildButton onClick={increment} label="Increment" />
<button onClick={() => setOther(c => c + 1)}>Other: {other}</button>
</div>
)
}何时使用
// ✅ 应该用 useMemo
const sorted = useMemo(() => {
return data.sort((a, b) => a.date - b.date)
}, [data])
// ❌ 不需要 useMemo
const fullName = firstName + ' ' + lastName // 简单的拼接
// ✅ 应该用 useCallback
const handleSubmit = useCallback((data: FormData) => {
api.submit(data)
}, [])
// ❌ 不需要 useCallback
const handleClick = () => alert('clicked') // 无依赖小结
useMemo缓存计算值,useCallback缓存函数引用。- 与
React.memo配合使用效果最佳。 - 不要在基本计算上用 useMemo(开销反而更大)。
- 优先解决真正存在的性能问题,不要提前优化。
上一节:useReducer 与复杂状态 下一节:自定义 Hook
