React 性能陷阱:不必要的 `useMemo` / `useCallback` —— 一篇看懂“负优化”
·
React 性能陷阱:不必要的 useMemo / useCallback —— 一篇看懂“负优化”
把每个变量都包进
useMemo,把每个函数都塞进useCallback,
结果……渲染更慢了?
本文用「原理 → 检测 → 重构」三步法,帮你拆掉“负优化”包袱。
一、为什么“无脑包”会负优化?
-
缓存成本 > 计算成本
创建闭包、比较依赖、存储结果都要时间。 -
依赖数组频繁变化
缓存失效 → 重新计算 → 比不缓存还慢。 -
引用变化导致子组件重渲染
过度useCallback让子组件 false positive。
二、3 个高频负优化现场
1️⃣ 廉价计算也包 useMemo
const count = useMemo(() => items.length, [items]); // ❌ 比 .length 还慢
重构:直接计算
const count = items.length; // ✅
2️⃣ 依赖不稳定,缓存永远失效
const filtered = useMemo(() => items.filter(x => x > threshold), [items, threshold]);
// ❌ items 每次都是新数组,缓存无用
重构:
- 用
useMemo上游稳定items - 或仅缓存 过滤函数:
const filtered = useMemo(() => items.filter(x => x > threshold), [threshold]);
// 保证 items 引用稳定(见下一条)
3️⃣ useCallback 让子组件“假更新”
const Parent = ({ data }) => {
const handleClick = useCallback(() => {
console.log(data);
}, [data]);
return <Child onClick={handleClick} />;
};
问题:data 变化 → handleClick 变化 → Child 重渲染(即使 Child 是纯组件)。
重构:如果 Child 不需要数据,拆离依赖:
const handleClick = useCallback(() => {
console.log(data);
}, [data]); // ✅ 保持依赖,但 Child 用 React.memo + 稳定 props
或把数据抽离到 Context:
const handleClick = useCallback(() => {
const data = useDataContext();
console.log(data);
}, []); // ✅ 无依赖
三、性能检测 2 件套
| 工具 | 用法 | 效果 |
|---|---|---|
| React DevTools Profiler | 录制渲染 → 查看“Why did this render?” | 发现多余重渲染 |
| ahooks/useWhyDidYouUpdate | 打印变化 props | 定位不稳定引用 |
四、决策树(贴在屏幕)
计算重吗? → 否 → 不用
│
是 → 依赖稳定? → 否 → 不用
│
是 → 子组件纯? → 是 → useMemo/useCallback
五、一句话总结
只给「重计算 + 稳定依赖 + 纯子组件」三件套才用缓存,
其余直接写——让 React 飞一会儿。
最后问候亲爱的朋友们,并邀请你们阅读我的全新著作
📚 《 React开发实践:掌握Redux与Hooks应用 》

更多推荐
所有评论(0)