• 隐私政策
  • 联系我们
  • 关于我们
2026 年 7 月 22 日 星期三
聚赢方舟
广告
  • 首页
  • 快讯 7x24
  • 行业新闻
  • 商业动态
  • 股市风云
  • 期货研报
  • 基金财讯
  • 贵金属
No Result
View All Result
  • 首页
  • 快讯 7x24
  • 行业新闻
  • 商业动态
  • 股市风云
  • 期货研报
  • 基金财讯
  • 贵金属
No Result
View All Result
聚赢方舟
No Result
View All Result
Home 基金财讯

别再写 useMemo 了——2026 年这 5 个 React 性能优化已经是反模式

by 聚赢方舟
2 天 ago
in 基金财讯
Reading Time: 16 mins read
A A
分享至微博分享给朋友


ADVERTISEMENT

打开任何一个 AI 辅助开发的 React 项目,你大概率会看到同样的画面:useMemo、useCallback、React.memo 铺天盖地,仿佛不写就会崩。

但 2026 年了,React Compiler 已经正式落地。你手动写的那些"优化",很多不但没用,还在拖慢你的代码。

先说结论:为什么要改

React Compiler(随 React 19 正式推出) 做了一件事:在编译阶段自动分析组件,在表达式级别做细粒度 memoization。

这意味着:你手写的 useMemo、useCallback、React.memo——编译器都能自动做,而且做得更细、更准确。

手动写的问题在于:

维度 手动优化 React Compiler
粒度 组件/Hook 级别 表达式级别 (更细)
准确性 依赖数组经常写错 编译器分析,不会遗漏
代码量 多 30-50% 包装代码 零额外代码
维护成本 依赖变了要手动更新 自动追踪
出错概率 依赖数组写错=缓存永远不更新 不存在这个问题

ESLint React v5.3 已经加了 no-unnecessary-use-memo 和 no-unnecessary-use-callback 规则——官方工具链都在告诉你别写了。

下面逐个看 5 个已经变成反模式的"优化"。

反模式 1:useMemo 包裹所有计算

最常见的 AI 生成代码模式:

function UserList({ users, filter }) {
  const filteredUsers = useMemo(() => {
    return users.filter(u => u.name.includes(filter));
  }, [users, filter]);

  const sortedUsers = useMemo(() => {
    return [...filteredUsers].sort((a, b) => a.name.localeCompare(b.name));
  }, [filteredUsers]);

  const stats = useMemo(() => ({
    total: sortedUsers.length,
    active: sortedUsers.filter(u => u.active).length,
  }), [sortedUsers]);

  return <div>{/* ... */}</div>;
}

三个 useMemo 做了一件事:过滤 + 排序 + 统计。

React Compiler 会自动分析这段代码的依赖关系,在 users 或 filter 没变的时候跳过所有计算。你手写三个 useMemo,其实在做编译器已经做了的事,同时还增加了:

  • 三个依赖数组的维护成本
  • 三个闭包的内存开销
  • 让代码可读性下降一半

2026 年的写法:

function UserList({ users, filter }) {
  const sorted = users
    .filter(u => u.name.includes(filter))
    .sort((a, b) => a.name.localeCompare(b.name));

  const stats = {
    total: sorted.length,
    active: sorted.filter(u => u.active).length,
  };

  return <div>{/* ... */}</div>;
}

干净、直接、编译器自动优化。

唯一例外: 如果你的计算确实很重 (比如对 10 万条数据做复杂聚合),并且你能用 React DevTools Profiler 证明它是瓶颈——那保留 useMemo。但 99% 的场景不是这样。

反模式 2:useCallback 包裹所有传给子组件的函数

function Dashboard() {
  const [count, setCount] = useState(0);

  const handleClick = useCallback(() => {
    setCount(c => c + 1);
  }, []);

  const handleReset = useCallback(() => {
    setCount(0);
  }, []);

  const handleExport = useCallback(() => {
    exportData(count);
  }, [count]);

  return (
    <>
      <Button onClick={handleClick} />
      <Button onClick={handleReset} />
      <ExportButton onClick={handleExport} />
    </>
  );
}

这段代码有三个问题:

  1. useCallback 本身不阻止子组件重渲染——除非子组件用了 React.memo
  2. 没有 React.memo 的情况下,useCallback 做的事就是"稳定引用"——但 React Compiler 已经能自动判断哪些组件需要跳过
  3. handleExport 的依赖数组里有 count,每次 count 变化它都会重建——useCallback 完全没起作用

更直接的问题是:AI 工具特别爱生成 useCallback。 因为训练数据里大量"React 性能优化最佳实践"文章都在教"传给子组件的函数一定要 useCallback"。这个建议在 2023 年可能有道理,在 2026 年就是噪音。

2026 年的写法:

function Dashboard() {
  const [count, setCount] = useState(0);

  return (
    <>
      <Button onClick={() => setCount(c => c + 1)} />
      <Button onClick={() => setCount(0)} />
      <ExportButton onClick={() => exportData(count)} />
    </>
  );
}

反模式 3:React.memo 包裹每个导出组件

const UserCard = React.memo(function UserCard({ user, onSelect }) {
  return (
    <div onClick={() => onSelect(user.id)}>
      <Avatar src={user.avatar} />
      <span>{user.name}</span>
    </div>
  );
});

React.memo 的逻辑是:props 没变就跳过渲染。问题是:

  1. React Compiler 已经在做同样的事,而且粒度更细——它可以跳过组件内部的部分表达式,不需要跳过整个组件
  2. React.memo*每次渲染都要做 props 浅比较*——如果 props 经常变,这个比较本身就是白白浪费
  3. 和 useCallback 配套才有意义——但如果你已经不写 useCallback 了,React.memo 也没必要了

一条很好判断的规则: 如果你的组件渲染成本低于 props 浅比较成本 (大部分简单组件都是这样),React.memo 就是负优化。

2026 年的写法: 直接导出,不包裹。

function UserCard({ user, onSelect }) {
  return (
    <div onClick={() => onSelect(user.id)}>
      <Avatar src={user.avatar} />
      <span>{user.name}</span>
    </div>
  );
}

反模式 4:useEffect + useState 做数据获取

这可能是最根深蒂固的反模式,也是 AI 生成代码最爱写的模式:

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    let cancelled = false;
    setLoading(true);

    fetchUser(userId)
      .then(data => {
        if (!cancelled) {
          setUser(data);
          setLoading(false);
        }
      })
      .catch(err => {
        if (!cancelled) {
          setError(err);
          setLoading(false);
        }
      });

    return () => { cancelled = true; };
  }, [userId]);

  if (loading) return <Spinner />;
  if (error) return <ErrorMessage error={error} />;
  return <div>{user.name}</div>;
}

25 行代码做一件事:获取数据。 而且还有这些隐患:

问题 后果
竞态条件 userId 快速变化时,旧请求覆盖新数据
瀑布请求 父组件获取完→子组件才开始获取→孙组件再等
无缓存 同一个 userId 每次挂载都重新请求
首次渲染闪白屏 loading 状态永远要经历 true→false
服务端渲染不友好 useEffect 在 SSR 阶段不执行

2026 年的写法 (用 TanStack Query):

function UserProfile({ userId }) {
  const { data: user, isPending, error } = useQuery({
    queryKey: ['user', userId],
    queryFn: () => fetchUser(userId),
  });

  if (isPending) return <Spinner />;
  if (error) return <ErrorMessage error={error} />;
  return <div>{user.name}</div>;
}

或者用 React 19 的 use:

function UserProfile({ userPromise }) {
  const user = use(userPromise);
  return <div>{user.name}</div>;
}

从 25 行到 3 行。 自动处理竞态、缓存、去重、后台刷新。

这不是风格偏好,是架构级的差距。 useEffect 做数据获取在 React 官方文档里已经被标注为不推荐。

反模式 5:过度拆分组件"优化性能"

我见过的一个极端案例:一个表单页面被拆成了 47 个组件——每个输入框一个组件、每个标签一个组件、每个校验提示一个组件。理由是"避免整个表单重渲染"。

这是对 React 渲染机制的误解。

组件拆分不等于性能优化。每多一个组件,React 就多了:

  • 一次 reconciliation 比较
  • 一个 Fiber 节点
  • 一次 props 序列化和比较

合理的拆分标准应该是:

场景 该拆 不该拆
组件超过 200 行 ✅ 可维护性 -
需要独立复用 ✅ 复用性 -
有独立的状态逻辑 ✅ 关注点分离 -
"这一块可能会频繁更新" ❌ 过早优化 ✅ 让 Compiler 处理
"拆小一点性能好" ❌ 错误假设 ✅ 用 Profiler 验证

真正该做的性能优化是状态下沉 (state co-location): 把 state 放到真正需要它的组件里,而不是提升到父组件然后靠拆分来避免重渲染。

// ❌ 状态提升 + 过度拆分
function Form() {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');
  return (
    <>
      <NameInput value={name} onChange={setName} />
      <EmailInput value={email} onChange={setEmail} />
    </>
  );
}

// ✅ 状态下沉,各管各的
function Form() {
  return (
    <>
      <NameInput />
      <EmailInput />
    </>
  );
}

function NameInput() {
  const [name, setName] = useState('');
  return <input value={name} onChange={e => setName(e.target.value)} />;
}

速查表:2026 年 React 性能优化决策

以前的"最佳实践" 2026 年的做法 什么时候还需要旧写法
useMemo 包裹计算 直接写,Compiler 处理 10 万 + 数据量的复杂聚合,且 Profiler 证实是瓶颈
useCallback 包裹函数 直接内联 传给第三方库且该库依赖引用稳定性
React.memo 包裹组件 直接导出 极重的渲染组件 (Canvas/3D),且 Profiler 证实
useEffect 获取数据 TanStack Query / use() / SWR 永远不需要 (没有例外)
过度拆分组件 按逻辑拆分 + 状态下沉 永远不需要 (没有例外)
手动 shouldComponentUpdate 删掉 2026 年了不应该还有 Class 组件

判断标准就一条:先用 React DevTools Profiler 跑一遍。没有证据证明是瓶颈的,就别优化。

升级 React Compiler 的最小步骤

如果你的项目还没启用 React Compiler:

npm install react-compiler-runtime
npm install -D babel-plugin-react-compiler

Babel 配置加一行:

{
  "plugins": [
    ["babel-plugin-react-compiler", {}]
  ]
}

Vite 用户:

// vite.config.js
import { reactCompiler } from 'react-compiler-runtime/vite';

export default {
  plugins: [reactCompiler()],
};

启用后,可以逐步删掉不必要的 useMemo/useCallback/React.memo。不用一次性全删——编译器和手动优化可以共存,只是手动的部分变成了冗余代码。

别让 AI 替你做决定

这篇文章本质上在说一件事:不要因为 AI 生成了 useMemo,你就不敢删它。

AI 代码生成器的训练数据里充满了 2020-2024 年的"最佳实践"文章。那些文章在当时是对的——React 没有编译器,手动 memoization 是唯一选择。但 2026 年了,工具变了,实践也要变。

用 AI 写代码没问题。但 Review 的时候,你需要知道什么该留、什么该删。

你的项目升级 React Compiler 了吗?升级后删了多少行 useMemo?评论区聊聊。

聚赢方舟

专业财经网站

聚赢方舟 (arkxx.com) 网站是长沙聚赢方舟文化传媒有限公司旗下运营的财经资讯门户网站。聚赢方舟致力于为用户提供全面而深入的财经资讯与金融数据分析。网站汇集了最新的市场行情、股票动态、投资策略以及经济趋势,为投资者和财经行业人士提供及时的新闻参考。网站通过高效的数据处理与分析工具,聚赢方舟帮助用户把握市场机会,优化投资决策。

此外,网站还定期发布专业的市场评估报告和财经评论,确保用户能够获得最准确的市场洞察。

方舟日历

2026 年 7 月
一 二 三 四 五 六 日
 12345
6789101112
13141516171819
20212223242526
2728293031  
« 6 月    

标签

中国 中国企业 也不 买了 互联网 假日 养老金 北大 千元 印度 反超 奶茶 家族 工龄 怎么回事 或将 房价 房贷 新能源 新闻 日本 更大 有什么 村官 来了 楼市 江苏 沙特 浙江 特斯拉 电动车 石油 美元 美国 美籍 节日 芯片 让人 越南 长假 防晒 阿里 阿里巴巴 院士 首富

© 2025 长沙聚赢方舟文化传媒有限公司 by 聚赢方舟 - 湘 ICP 备 2025135270 号-1

No Result
View All Result
  • Home

© 2025 长沙聚赢方舟文化传媒有限公司 by 聚赢方舟 - 湘 ICP 备 2025135270 号-1

此网站使用 cookie。继续使用本网站即表示您同意使用 cookie。访问隐私和 cookie 策略.。