什么是PureComponent
PureComponent
是React
使用类组件时做性能优化继承的一个组件,与Component
拥有相同的生命周期方法,用法也一模一样,不同的是PureComponent
重写了shouldComponentUpdate
函数,对组件的state
与props
做了一个浅比较,也就是说简单的比较了第一层数据是否相同,如果相同的话就返回false
,render
函数就不会再执行,从而提升性能,源码如下:
if (this._compositeType === CompositeTypes.PureClass) {
shouldUpdate = !shallowEqual(prevProps, nextProps) || ! shallowEqual(inst.state, nextState);
}
其中, shadowEqual
只会"浅"检查组件的 props
和 state
,这就意味着嵌套对象和数组是不会被比较的。虽然使用PureComponent
会带来性能提升,但不是所有组件都适用,如果使用不当,非但性能得不到提升,反而会降低。
什么情况下不适用
1.刷新数据列表时
// 父组件
export default Items extends React.Component {
state = {
items: []
}
componentDidMount() {
this.getItems()
}
getItems() {
this.props.dispatch({
type: 'getItems',
payload: {...}
}).then(data => {
this.setState({ items: data })
})
}
render() {
return (
<div>
<Button onClick={this.getItems}>刷新列表<Button>
{
this.state.items.map(item => <Item item={item} />)
}
</div>
)
}
}
// 子组件,注意这里继承了PureComponent
export default Item extends React.PureComponent {
render() {
return (
<div>{this.props.item.name}</div>
)
}
}
简单解析下代码,父组件Items
继承了React.Component
,并提供一个按钮可以点击刷新数据,子组件Item
继承了React.PureComponent
,希望在刷新数据的时候做一个比较,数据不变的话就不重新渲染,但由于每次请求回来的数据都是新的,虽然数据不变,但是对象的引用改变了。此时做浅比较就没有意义了,完全是在浪费时间,还不如直接继承Component
。
2.父组件中使用PureComponent
// 父组件, 注意这里继承了PureComponent
export default Items extends React.PureComponent {
state = {
items: [
{key: 'dsl', name: 'dsl'}
]
}
addItem() {
const { items } = this.state;
items.push({ {key: 'dsl1', name: 'dsl1'}})
this.setState({ items })
}
render() {
return (
<div>
<Button onClick={this.addItem}>新增数据<Button>
{
this.state.items.map(item => <Item item={item} />)
}
</div>
)
}
}
// 子组件
export default Item extends React.PureComponent {
render() {
return (
<div>{this.props.item.name}</div>
)
}
}
父组件中继承了React.PureComponent
,并且提供一个按钮,功能是点一下新增1条数据,希望的效果是最终页面上会渲染新增的数据,但最终结果是点击了页面没什么反应,还是只有1条数据。原因是父组件在做浅比较时由于items
的引用没有改变,执行shouldComponentUpdate
时返回了false
,因此子组件不会在重新渲染,造成了Bug
。
什么情况下适用
上面是我想到不适用的2种情况,其他情况暂时没有想到,欢迎大佬补充,下面介绍适用的情况。
1.点击一个按钮,希望能够控制子组件展示
// 父组件,注意这里继承了PureComponent
export default Items extends React.PureComponent {
state = {
isShow: false
}
showItem() {
this.setState({ isShow: true })
}
render() {
return (
<div>
<Button onClick={this.showItem}>显示子组件<Button>
{
this.state.isShow && <Item />
}
</div>
)
}
}
// 子组件
export default Item extends React.Component {
render() {
return (
<div>我是子组件,我展示了</div>
)
}
}
父组件继承了React.PureComponent
,并且新增一个按钮,希望点击按钮时子组件能够展示,效果是子组件展示了。第一次点击按钮时,由于isShow
是false
,点击按钮后isShow
变为了true
,这时由于继承了PureComponent
,浅比较发现前后的isShow
发生了变化,shouldComponentUpdate
方法返回了true
,子组件得到了渲染。第二次点击按钮时,子组件不会再渲染,因为isShow
做了浅比较后发现没有改变,从而性能得到了提升。如果父组件使用的是Component
,每点击一次就要子组件就要重新渲染一次。
2.子组件接收的数据是非引用类型
// 父组件
export default Items extends React.Component {
state = {
items: []
}
componentDidMount() {
this.getItems()
}
getItems() {
this.props.dispatch({
type: 'getItems',
payload: {...}
}).then(data => {
this.setState({ items: data })
})
}
render() {
return (
<div>
<Button onClick={this.getItems}>刷新列表<Button>
{
// 注意,这里传递的是非引用类型
this.state.items.map(item => <Item name={item.name} />)
}
</div>
)
}
}
// 子组件,注意这里继承了PureComponent
export default Item extends React.PureComponent {
render() {
return (
<div>{this.props.name}</div>
)
}
}
同样是父组件Items
继承了React.Component
,并提供一个按钮可以点击刷新数据,子组件Item
继承了React.PureComponent
,希望在刷新数据的时候做一个比较,数据不变的话就不重新渲染。不同的是父组件传递的是一个非引用类型的数据name
,点击按钮刷新列表,做了浅比较后发现name没有改变,因此不会重新渲染。
总结
PureComponent
给我的感觉是使用场景十分有限,特别是针对引用类型的数据不会带来什么好处,反而是白白做了一次比较消耗性能,在平时开发中最好不要滥用,在使用是时最好思考一下是否能够带来性能提升,欢迎大家在评论中补充其他的适用及不适用的情况。