在 React 中,useMemo
和 useCallback
是用于性能优化的 Hooks,它们通过缓存计算结果和函数引用来减少不必要的重渲染和计算。下面通过具体示例说明它们的使用场景和效果:
1. useMemo
:缓存计算结果
当组件中有昂贵的计算操作时,useMemo
可以缓存计算结果,避免每次渲染都重新计算。
import { useMemo, useState } from 'react';
function ExpensiveCalculation({ numbers }) {
// 模拟一个昂贵的计算(如大量数据处理)
const calculateSum = (nums) => {
console.log("重新计算总和...");
return nums.reduce((acc, num) => acc + num, 0);
};
// 不使用 useMemo:每次渲染都会重新计算
// const total = calculateSum(numbers);
// 使用 useMemo:只有 numbers 变化时才重新计算
const total = useMemo(() => calculateSum(numbers), [numbers]);
return <div>总和: {total}</div>;
}
function App() {
const [count, setCount] = useState(0);
const [numbers] = useState([1, 2, 3, 4, 5]);
return (
<div>
<button onClick={() => setCount(count + 1)}>计数: {count}</button>
<ExpensiveCalculation numbers={numbers} />
</div>
);
}
效果:点击按钮更新 count
时,ExpensiveCalculation
组件会重新渲染,但由于 numbers
没有变化,useMemo
会返回缓存的计算结果,避免了 calculateSum
函数的重复执行。
2. useCallback
:缓存函数引用
当向子组件传递回调函数时,useCallback
可以缓存函数引用,防止因函数引用变化导致子组件不必要的重渲染。
import { useCallback, useState, memo } from 'react';
// 使用 memo 包装子组件,仅在 props 变化时重渲染
const UserItem = memo(({ user, onDelete }) => {
console.log(`UserItem ${user.id} 渲染`);
return (
<div>
{user.name}
<button onClick={() => onDelete(user.id)}>删除</button>
</div>
);
});
function UserList() {
const [users, setUsers] = useState([
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" }
]);
const [filter, setFilter] = useState("");
// 不使用 useCallback:每次渲染都会创建新的函数引用
// const handleDelete = (id) => {
// setUsers(users.filter(user => user.id !== id));
// };
// 使用 useCallback:只有依赖项变化时才创建新函数
const handleDelete = useCallback((id) => {
setUsers(prevUsers => prevUsers.filter(user => user.id !== id));
}, []); // 空依赖数组:函数引用始终不变
return (
<div>
<input
placeholder="筛选"
value={filter}
onChange={(e) => setFilter(e.target.value)}
/>
{users.map(user => (
<UserItem
key={user.id}
user={user}
onDelete={handleDelete}
/>
))}
</div>
);
}
效果:输入筛选内容时,filter
状态变化会导致 UserList
重渲染。由于 handleDelete
被 useCallback
缓存,其引用不会变化,因此 UserItem
组件不会不必要地重渲染。
关键区别与使用原则
特性 | useMemo |
useCallback |
---|---|---|
作用 | 缓存计算结果 | 缓存函数引用 |
返回值 | 缓存的值 | 缓存的函数 |
典型场景 | 昂贵的计算操作 | 传递给子组件的回调函数 |
使用原则:
- 不要过早优化:只有当确实存在性能问题时才使用它们
- 避免过度使用:缓存本身也有开销,适用于频繁重渲染的场景
- 正确设置依赖项:依赖数组必须包含所有函数内部使用的外部变量
总结
useMemo
解决的是重复计算的性能问题useCallback
解决的是不必要的重渲染问题(通常与memo
配合使用)- 两者都是通过「缓存」来减少资源消耗,优化 React 应用的性能