1. React.memo 是什么?
React.memo 是一个高阶组件(HOC),它的作用是:"缓存"你的组件。
- 当父组件重新渲染时,
React.memo会**浅层比较(Shallow Compare)**子组件的props。 - 如果
props没有发生变化,子组件就不会重新渲染,直接复用上一次的结果。 - 如果
props发生了变化,子组件才会重新渲染。
2. 把"普通函数"作为 props 传进去,会发生什么?
假设你有这样的代码:
jsx
// ❌ 子组件被 memo 包裹
const Child = React.memo(({ onClick }) => {
console.log('子组件渲染了');
return <button onClick={onClick}>点我</button>;
});
function Parent() {
const [count, setCount] = useState(0);
// ⚠️ 这是一个普通函数,每次 Parent 渲染都会重新创建
const handleClick = () => {
console.log('点击了');
};
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>增加</button>
<Child onClick={handleClick} /> {/* 把函数传给了 Child */}
</div>
);
}
执行流程:
- 点击"增加"按钮,
count变化,Parent重新渲染。 Parent重新执行,handleClick被重新创建(内存地址变了)。React.memo比较Child的props:- 上一次的
onClick是函数A(地址0x001)。 - 这一次的
onClick是函数B(地址0x002)。 - 浅层比较:
0x001 === 0x002是false。
- 上一次的
- 结论 :
React.memo认为props变了,所以子组件重新渲染。
结果 :你用了 React.memo 想优化性能,结果因为传入了"每次新建的函数"导致优化完全失效(子组件依然跟着父组件一起渲染)。
3. 那怎么解决?
如果你希望 React.memo 生效,必须保证传给子组件的函数引用不变。
解决方法就是用 useCallback 缓存这个函数:
jsx
// ✅ 修复后的代码
function Parent() {
const [count, setCount] = useState(0);
// useCallback 保证:只要依赖 [] 不变,handleClick 永远是同一个引用
const handleClick = useCallback(() => {
console.log('点击了');
}, []);
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>增加</button>
<Child onClick={handleClick} /> {/* 引用不变 ✅ */}
</div>
);
}
现在:
React.memo比较两次的onClick,发现是同一个对象(引用地址相同)。- 结论:
props没变 → 子组件不重新渲染,性能优化生效。
4. 类似地,对象和数组也会导致这个问题
jsx
// ❌ 传递每次新建的对象,也会让 memo 失效
<Child config={{ theme: 'dark' }} />
// ✅ 使用 useMemo 缓存对象
const config = useMemo(() => ({ theme: 'dark' }), []);
<Child config={config} />
一句话总结
"作为 props 传递给 React.memo 子组件" 就是指:当你把一个引用类型 (函数、对象、数组)传给被
React.memo包裹的子组件时,如果这个引用在父组件每次渲染时都重新创建,React.memo的浅层比较会认为 props 变了,导致子组件跟着重新渲染,失去优化意义。
要解决这个问题,你必须用 useCallback (缓存函数)或 useMemo (缓存对象/数组)来保持引用稳定。这也是为什么 useCallback 和 useMemo 几乎总是和 React.memo 成对出现的原因。