You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用React.memo、useMemo、useCallback避免对象传参引发的子组件意外重渲染

问题根因

React.memo默认对props做浅比较,仅判断props的内存引用是否相等。你的代码中App组件每次重渲染时,都会在函数体内重新创建sourceData对象,哪怕对象内容完全一致,它的引用也是全新的,因此传给TextExample3的data prop每次都无法通过浅比较,触发子组件意外重渲染。

优雅解决方案

下面两种方案都可以替代JSON序列化的hack实现:

方案1:给React.memo传入自定义比较函数

不用修改父组件代码,直接在子组件侧自定义props的比较规则:

// 基础写法,按属性逐个比较
const TextExample3 = React.memo(({data, infoText}) => {
  console.log(infoText);
  return (
    <p>
      {infoText} {data.name} {data.surname}
    </p>
  );
}, (prevProps, nextProps) => {
  // 返回true表示props无变化,不需要重渲染
  return prevProps.infoText === nextProps.infoText 
    && prevProps.data.name === nextProps.data.name 
    && prevProps.data.surname === nextProps.data.surname
});

如果对象层级深,可以直接用深比较工具简化代码:

import isEqual from 'lodash/isEqual';

const TextExample3 = React.memo(({data, infoText}) => {
  // 组件原有逻辑不变
}, (prevProps, nextProps) => isEqual(prevProps, nextProps));

注意:自定义比较函数的返回值和shouldComponentUpdate相反,返回true代表跳过重渲染,返回false代表触发重渲染。

方案2:父组件用useMemo缓存对象引用

不用修改子组件代码,在父组件侧保证对象引用稳定,默认的浅比较就能生效:

const App = () => {
  console.log('----- NEW RENDER -----');
  const [forceRerenderNumber, setForceRerenderNumber] = useState(0);

  // 用useMemo缓存对象,依赖项为空时仅会在首次渲染时创建一次
  const sourceData = React.useMemo(() => ({
    name: 'Joe',
    surname: 'Moore',
  }), []);

  // 剩余原有代码不变
};

如果sourceData的内容依赖其他state/prop,只需要把对应变量加到useMemo的依赖数组中即可,只有依赖变化时才会生成新的对象。

方案选型建议
  • 如果子组件复用性高,接收的对象结构不固定:优先选方案2,父组件自行缓存传参,子组件不需要兼容不同结构的比较逻辑
  • 如果子组件固定接收特定结构的参数:优先选方案1,父组件不需要额外做缓存处理
  • 如果对象层级极深、数据量极大:优先选方案2,深比较的开销可能远高于子组件重渲染的开销,缓存引用的性能收益更高

你之前尝试useMemo没生效,大概率是没有把对象的创建逻辑包在useMemo中,或是依赖数组填写错误导致的。


内容的提问来源于stack exchange,提问作者CodeJoe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.16 12:24:00