React.memo 与 useMemo
约 379 字大约 1 分钟
布欧-Lewyon
2026-05-15
React.memo
import { memo, useState } from 'react'
// ❌ 每次 Parent 渲染,ExpensiveComponent 都重渲染
function ExpensiveComponent({ name }: { name: string }) {
console.log('Rendered:', name)
return <div>{name}</div>
}
// ✅ Props 不变时跳过重渲染
const MemoizedComponent = memo(function ExpensiveComponent({ name }: { name: string }) {
console.log('Rendered:', name)
return <div>{name}</div>
})
function Parent() {
const [count, setCount] = useState(0)
const [name, setName] = useState('Alice')
return (
<div>
<button onClick={() => setCount(c => c + 1)}>Count: {count}</button>
<button onClick={() => setName('Bob')}>Change Name</button>
<MemoizedComponent name={name} />
</div>
)
}useMemo + memo 配合
interface User { id: number; name: string }
function UserList({ users, filter }: { users: User[]; filter: string }) {
// ✅ 只有 users 或 filter 变化才重算
const filtered = useMemo(() => {
return users.filter(u => u.name.includes(filter))
}, [users, filter])
return (
<ul>
{filtered.map(u => (
<UserItem key={u.id} user={u} />
))}
</ul>
)
}
const UserItem = memo(function UserItem({ user }: { user: User }) {
console.log('Rendered:', user.name)
return <li>{user.name}</li>
})渲染优化原则
// 1. 状态下移(让变化范围最小化)
function App() {
const [count, setCount] = useState(0)
return (
<div>
<Counter count={count} onIncrement={() => setCount(c => c + 1)} />
<StaticComponent /> {/* 不受 count 影响 */}
</div>
)
}
// 2. 避免 Props 透传
// ❌ 每次 render 创建新对象
<MemoizedItem style={{ color: 'red' }} />
// ✅ 提取常量
const STYLE = { color: 'red' }
<MemoizedItem style={STYLE} />
// 3. Context 分割
// 频繁变化的值(typing)单独 Context
// 稳定值(theme)单独 Context小结
React.memo浅比较 Props,跳过不必要的重渲染。useMemo缓存计算结果/对象引用,配合 memo 使用。- 状态下移:让频繁变化的状态局限在小组件内。
- 不提前优化:先用 Profiler 确认瓶颈再动手。
上一节:Cypress E2E 测试 下一节:lazy / Suspense 与虚拟列表
