做过 React 项目的应该都遇到过这种情况:开发时页面丝般顺滑,但一上线数据量大了就卡得要死。点个按钮要等三秒,比我家猫起床都慢。
别急着甩锅给后端,今天说个 React 里经常被忽视但很有用的东西 —— useMemo
。
这玩意儿名字听起来像 "有记忆",其实就是组件的省电模式。
useMemo 是什么#
想象你有个同事,每次问他复杂问题都要算半天。关键是每问一次他就重算一遍,哪怕数据压根没变。
useMemo
就像给这个同事装了个记忆:上次算过的结果记着呢,你要不要直接用?
本质就是缓存计算结果,只要依赖没变就直接返回旧结果,不重复计算。
语法长这样:
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
只有当a
或b
改了才重新算,否则直接返回上次的结果。
什么时候用#
1. 计算很重的时候#
比如处理5000
条订单数据做排序、过滤、统计。这种操作每次都跑风扇都要起飞,而组件一天要渲染几十次。
const processedData = useMemo(() => {
return heavyProcess(orders); // 耗时操作
}, [orders]); // 只有orders变了才重算
这样就从每次渲染都算变成只算一次。
2. 给子组件传对象或数组#
子组件用了React.memo
但还是一直重渲染?很可能是因为传过去的 prop 每次都是新的引用。
比如这样:
// 每次渲染都创建新数组,哪怕内容一样
<MyComponent filters={[{ type: "active" }]} />
解决方法:
const stableFilters = useMemo(() => [{ type: "active" }], []);
<MyComponent filters={stableFilters} />;
这样对象地址就不变了,React.memo
才能正常工作。
3. 作为其他 Hook 的依赖项#
const config = { userId, theme };
useEffect(() => {
fetchUserData(config);
}, [config]); // 问题来了!
每次渲染config
都是新对象,useEffect
就会重复触发。
解决:
const config = useMemo(() => ({ userId, theme }), [userId, theme]);
useEffect(() => {
fetchUserData(config);
}, [config]);
现在只有userId
或theme
变了config
才更新。
useMemo vs useCallback#
这两个容易搞混:
Hook | 缓存的是啥? | 使用场景 |
---|---|---|
useMemo | 一个值 | 复杂计算、对象 / 数组、作为依赖 |
useCallback | 一个函数本身 | 防止函数变化导致子组件重渲染 |
// useMemo:缓存"计算结果"
const total = useMemo(() => items.reduce(sum), [items]);
// useCallback:缓存"函数本身"
const handleClick = useCallback(() => doSomething(id), [id]);
常见坑点#
依赖项写错#
useMemo(() => expensive(), [{}]); // 每次都是新对象!
useMemo(() => expensive(), [[]]); // 每次都是新数组!
确保依赖项是稳定的值。
滥用 useMemo#
不是所有计算都要缓存。如果只是简单的a + b
,用useMemo
反而浪费。
React 官方说过:过早优化是万恶之源。先找到性能瓶颈再用useMemo
。
总结#
什么时候用:
- 计算很重
- 传给
memo
子组件的对象 / 数组 - 作为其他
Hook
的依赖项
记住:它是瑞士军刀,不是创可贴。
页面卡了别急着换框架,先看看是不是该给 useMemo
发工资了。