PureComponent
#react
目录
- 1. 总结
- 2. PureComponent 的定义
- 3. PureComponent vs Component
- 4. 浅比较的工作原理
- 5. 使用场景和注意事项
- 6. 在函数组件中的等效实现
- 7. 常见陷阱和优化
1. 总结
- PureComponent 自动实现了
shouldComponentUpdate
- 支持所有常规的 React 生命周期方法
- Component vs PureComponent
- PureComponent:
- 会对 props 和 state 进行==浅比较==,只有在数据真正发生变化时才重新渲染
- Component:
- 默认情况下,只要父组件重新渲染,组件就会重新渲染
- PureComponent:
- 可使用 ==React.memo== 来实现类似 PureComponent 的功能
- 因为它也接受第二个参数
- 一些注意点
- 对象属性尽量单独提出,比如 style,每次渲染都创建新对象
- 建议,将对象提到组件外部或使用 useMemo
- 数组属性使用 map 时,会新建数组
- 函数属性不要内联
- 内联函数会导致 PureComponent 失效
- 对象属性尽量单独提出,比如 style,每次渲染都创建新对象
2. PureComponent 的定义
PureComponent 是 React 提供的一个基础组件类,它自动实现了 shouldComponentUpdate
生命周期方法,通过浅比较(shallow comparison)props 和 state
来决定是否需要重新渲染组件
PureComponent 完全支持所有常规的 React 生命周期方法。
- 它主要通过自动实现
shouldComponentUpdate()
来优化性能,但不会限制你使用其他生命周期钩子。
3. PureComponent vs Component
// 普通 Component
class RegularComponent extends React.Component {
render() {
console.log("Regular Component render");
return <div>{this.props.value}</div>;
}
}
// PureComponent
class PureComponentExample extends React.PureComponent {
render() {
console.log("Pure Component render");
return <div>{this.props.value}</div>;
}
}
3.1. 更新机制
- Component:默认情况下,只要父组件重新渲染,组件就会重新渲染
- PureComponent:会对 props 和 state 进行浅比较,只有在数据真正发生变化时才重新渲染
3.2. 性能影响
- Component:可能导致不必要的重渲染
- PureComponent:通过浅比较避免不必要的重渲染,提高性能
4. 浅比较的工作原理
class Example extends React.PureComponent {
state = {
data: { count: 0 },
array: [1, 2, 3],
};
// 浅比较的情况
goodUpdate = () => {
this.setState({
// 创建新对象,会触发重新渲染
data: { count: 1 },
// 创建新数组,会触发重新渲染
array: [...this.state.array, 4],
});
};
// 浅比较可能出问题的情况
problematicUpdate = () => {
const { data, array } = this.state;
// 直接修改对象,不会触发重新渲染
data.count = 1;
// 直接修改数组,不会触发重新渲染
array.push(4);
this.setState({ data, array });
};
}
5. 使用场景和注意事项
5.1. 适合使用 PureComponent
的场景
// 展示型组件,props 是简单类型
class UserInfo extends React.PureComponent {
render() {
return (
<div>
<h2>{this.props.name}</h2>
<p>{this.props.email}</p>
</div>
);
}
}
5.2. 不适合使用 PureComponent
的场景
// 频繁更新的组件
class Timer extends React.Component { // 使用普通 Component
state = { time: new Date() };
componentDidMount() {
this.timer = setInterval(() => {
this.setState({ time: new Date() });
}, 1000);
}
componentWillUnmount() {
clearInterval(this.timer);
}
render() {
return <div>{this.state.time.toLocaleTimeString()}</div>;
}
}
6. 在函数组件中的等效实现
在函数组件中,我们使用 React.memo 来实现类似 PureComponent 的功能:
// 使用 React.memo 创建类似 PureComponent 的效果
const MemoizedComponent = React.memo(function UserInfo({ name, email }) {
return (
<div>
<h2>{name}</h2>
<p>{email}</p>
</div>
);
});
// 自定义比较逻辑
const CustomMemoized = React.memo(
function UserInfo({ user }) {
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
},
(prevProps, nextProps) => {
// 返回 true 则不重新渲染
return prevProps.user.id === nextProps.user.id;
}
);
7. 常见陷阱和优化
7.1. 对象属性的问题
[!danger] 关键的,
style={{ color: 'red' }}
不好!
class Parent extends React.Component {
render() {
// 不好的做法:每次渲染都创建新对象
return <PureChild style={{ color: 'red' }} />;
// 好的做法:将对象提到组件外部或使用 useMemo
return <PureChild style={styles} />;
}
}
const styles = { color: 'red' };
7.2. 数组属性的处理
class ListComponent extends React.PureComponent {
render() {
const { items } = this.props;
return (
<ul>
{/* 不好的做法:map 创建新数组 */}
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
);
}
}
// 优化:将 map 结果缓存
class OptimizedList extends React.PureComponent {
memoizedItems = null;
getMemoizedItems(items) {
if (!this.memoizedItems || this.props.items !== items) {
this.memoizedItems = items.map((item, index) => (
<li key={index}>{item}</li>
));
}
return this.memoizedItems;
}
render() {
return <ul>{this.getMemoizedItems(this.props.items)}</ul>;
}
}
7.3. 函数属性的处理
class Parent extends React.Component {
// 好的做法:使用类方法
handleClick = () => {
// 处理点击
};
render() {
return (
// 不好的做法:内联函数会导致 PureComponent 失效
<PureChild onClick={() => this.handleClick()} />
// 好的做法:传递类方法引用
<PureChild onClick={this.handleClick} />
);
}
}