React 性能陷阱:不必要的 useMemo / useCallback —— 一篇看懂“负优化”

把每个变量都包进 useMemo,把每个函数都塞进 useCallback
结果……渲染更慢了
本文用「原理 → 检测 → 重构」三步法,帮你拆掉“负优化”包袱。


一、为什么“无脑包”会负优化?

  1. 缓存成本 > 计算成本
    创建闭包、比较依赖、存储结果都要时间。

  2. 依赖数组频繁变化
    缓存失效 → 重新计算 → 比不缓存还慢

  3. 引用变化导致子组件重渲染
    过度 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应用 》

在这里插入图片描述

更多推荐