在 React 中,性能优化是一个重要的主题,特别是在复杂的组件树中。本文将演示如何在同一个父组件中使用 useMemo
和 React.memo
来优化子组件的渲染。
1. 组件结构
创建一个父组件,包含两个子组件:
- MemoChild:使用
React.memo
进行优化。 - ExpensiveChild:使用
useMemo
缓存。
示例:
import React, { useState, useMemo } from 'react';
// 子组件,使用 React.memo
const MemoChild = React.memo(({ data }) => {
console.log('MemoChild rendered');
return <div>{data}</div>;
});
// 子组件,使用 useMemo 缓存
const ExpensiveChild = ({ data }) => {
console.log('ExpensiveChild rendered');
return <div>{data}</div>;
};
// 父组件
function Parent() {
const [count, setCount] = useState(0);
const [data, setData] = useState('Initial Data');
// 使用 useMemo 来缓存 ExpensiveChild
const memoizedExpensiveChild = useMemo(() => {
console.log('Calculating ExpensiveChild');
return <ExpensiveChild data={data} />;
}, [data]);
return (
<div>
<h1>Parent Component</h1>
<MemoChild data={`Count: ${count}`} />
{memoizedExpensiveChild}
<button onClick={() => setCount(count + 1)}>Increment Count</button>
<button onClick={() => setData('New Data')}>Update Data</button>
</div>
);
}
export default Parent;
2. 组件功能解析
MemoChild
- 使用
React.memo
包裹,只有当其data
属性变化时才会重新渲染。
ExpensiveChild
- 普通的子组件,父组件使用
useMemo
来缓存其实例。 - 只有当
data
属性变化时,useMemo
会重新计算并返回新的组件实例。
Parent
- 管理两个状态:
count
和data
。 - 点击“Increment Count”按钮时,只有
MemoChild
会更新,而ExpensiveChild
的引用保持不变。 - 点击“Update Data”按钮时,
ExpensiveChild
会重新渲染,因为data
发生了变化。
3. 运行结果
点击“Increment Count”按钮
count
状态更新,MemoChild
会重新渲染,控制台输出:MemoChild rendered
ExpensiveChild
不会重新渲染,控制台不会输出。
点击“Update Data”按钮
data
状态更新,ExpensiveChild
会重新渲染,控制台输出:Calculating ExpensiveChild ExpensiveChild rendered
4. useMemo
和 React.memo
对比:
useMemo
- Hook钩子函数,用于缓存计算结果。只有当依赖项变化时,才会重新计算。
使用useMemo
缓存ExpensiveChild
实例,避免不必要的重新渲染。
React.memo
- 高阶组件,缓存组件的渲染结果。只有当传入的 props 发生变化时,组件才会重新渲染。
- 适用于根据 props 变化控制渲染的场景。
5. 渲染逻辑总结
基本数据类型(如 number
、string
、boolean
)
- 当组件的 props 是基本数据类型时,如果其值未发生变化,
React.memo
不会重新渲染组件。
引用数据类型(如 object
、array
)
- 当组件的 props 是引用数据类型时,
React.memo
会进行浅比较。 - 如果引用相同,组件不会重新渲染;如果引用不同,组件会重新渲染。
6. 关键点
- 基本数据类型的比较:只要值相同,组件不会重新渲染。
- 引用数据类型的比较:如果引用相同,组件不会重新渲染;如果引用不同(无论内容是否相同),组件会重新渲染。