React.memo
是 React 提供的 高阶组件(HOC),用于优化函数组件的性能。它通过浅比较(shallow comparison)props,只有当 props
发生变化时才会重新渲染组件,从而减少不必要的重新渲染,提高应用的性能。
目录
1. React.memo 的基本用法
使用 React.memo
只需将函数组件传递给 React.memo()
,它会返回一个优化后的组件。
示例:使用 React.memo
import React from 'react';
// 普通函数组件
function MyComponent({ name }) {
console.log('MyComponent 渲染');
return <h1>你好, {name}!</h1>;
}
// 使用 React.memo 进行优化
export default React.memo(MyComponent);
解释:
React.memo(MyComponent)
:创建一个**记忆化(memoized)**组件,只有props.name
发生变化时才会重新渲染。- 如果
props.name
没有变化,组件不会重新渲染,控制台不会打印"MyComponent 渲染"
。
测试 React.memo
效果
我们用一个父组件来测试 MyComponent
是否会重新渲染:
import React, { useState } from 'react';
import MyComponent from './MyComponent';
function ParentComponent() {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>增加计数: {count}</button>
<MyComponent name="张三" />
</div>
);
}
export default ParentComponent;
运行结果:
- 点击按钮增加
count
时,ParentComponent
会重新渲染,但MyComponent
不会重新渲染(因为name="张三"
没有变)。 - 如果
MyComponent
未使用React.memo
,它会随ParentComponent
一起重新渲染。
2. React.memo 的工作原理
React.memo
使用浅比较(shallow comparison)来检查 props
是否变化:
- 基本数据类型(string、number、boolean):直接比较值是否相等。
- 引用类型(对象、数组、函数):仅比较引用是否相同,而不会深度比较其内容。
示例:浅比较导致的问题
import React from 'react';
const MyComponent = React.memo(({ data }) => {
console.log('MyComponent 渲染');
return <p>{data.value}</p>;
});
export default function App() {
const obj = { value: 'Hello' };
return <MyComponent data={obj} />;
}
问题:
- 每次
App
重新渲染时,obj
都是新的引用,即使value
没变,React.memo
仍然会重新渲染MyComponent
。
解决方案: 使用 useMemo
保持 data
引用不变:
import React, { useMemo } from 'react';
export default function App() {
const obj = useMemo(() => ({ value: 'Hello' }), []);
return <MyComponent data={obj} />;
}
3. React.memo 的应用场景
适用场景
✅ 子组件渲染与父组件状态无关
- 组件
props
不变,但因父组件重新渲染而被迫更新时,使用React.memo
避免不必要的渲染。
✅ 列表项
React.memo
在长列表组件(如map
渲染的列表项)中非常有用,可以减少渲染开销。
✅ 性能优化
- 适用于高频次渲染的组件(如搜索框、实时更新数据等)。
4. React.memo 与 useCallback
如果 props
传递的是函数,React.memo
不会阻止子组件重新渲染,因为函数是引用类型,每次渲染都会创建新函数。
示例:父组件导致子组件重复渲染
import React, { useState } from 'react';
const ChildComponent = React.memo(({ onClick }) => {
console.log('ChildComponent 渲染');
return <button onClick={onClick}>点击</button>;
});
function ParentComponent() {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>增加计数: {count}</button>
<ChildComponent onClick={() => console.log('Clicked!')} />
</div>
);
}
export default ParentComponent;
结果:
- 每次
ParentComponent
重新渲染时,ChildComponent
都会重新渲染,因为onClick={() => console.log('Clicked!')}
是新函数,导致props
变化。
解决方案:使用 useCallback
用 useCallback
让 onClick
保持相同引用,避免子组件重新渲染:
import React, { useState, useCallback } from 'react';
const ChildComponent = React.memo(({ onClick }) => {
console.log('ChildComponent 渲染');
return <button onClick={onClick}>点击</button>;
});
function ParentComponent() {
const [count, setCount] = useState(0);
// 使用 useCallback 让 onClick 的引用保持不变
const handleClick = useCallback(() => {
console.log('Clicked!');
}, []);
return (
<div>
<button onClick={() => setCount(count + 1)}>增加计数: {count}</button>
<ChildComponent onClick={handleClick} />
</div>
);
}
export default ParentComponent;
结果:
ChildComponent
不会重复渲染,因为handleClick
只在组件挂载时创建一次,避免props
变化。
5. React.memo 的局限性
❌ 只针对函数组件生效,类组件不能使用 React.memo
。
❌ 默认只做浅比较,深层次对象或数组变化时需要 useMemo
或 useCallback
处理。
❌ 与 useState
、useEffect
一起使用时要注意,因为 state
更新仍会触发 re-render
。
❌ 不适用于所有组件,如果组件本身渲染代价很低,使用 React.memo
可能反而会增加性能开销。
6. 参考资料
出站链接
站内链接
总结
✅ React.memo
用于优化函数组件,防止不必要的重新渲染。
✅ 浅比较 props
,只有 props
变化时才会重新渲染。
✅ 与 useCallback
、useMemo
结合使用,防止引用类型 props
造成无效渲染。
✅ 适用于高性能优化场景,但不适用于所有组件,需要结合实际需求使用。
发表回复