本文已参与「新人创作礼」活动,一起开启掘金创作之路。
前言
本文主要内容:了解shouldComponentUpdate()、React.PureComponet以及React.memo()三种防止组件重复渲染的方法
项目预览与分析
直接从console.log中也可以推断出,我们总共定义了四个组件,每个组件调用两次它下一层的组件,比如App组件调用了两次GrandParent组件,GrandParent组件调用了两次Parent组件,以此类推
只不过还在App组件中定义了一个按钮点击函数,用来每次方便地重新渲染App组件
所以代码也确实如此,App.js中的代码如下
import React, {Component} from "react"
import GrandParent from "./GrandParent"
class App extends Component {
state = { count: 0 }
increment = () => this.setState(prevState => ({count: prevState.count + 1}))
render() {
console.log("[GP] [P] [C] [GC] APP just rendered")
return (
<div>
<button onClick={this.increment}>+1</button>
<h2>{this.state.count}</h2>
<p>I'm the App component</p>
<GrandParent />
<GrandParent />
</div>
)
}
}
export default App
以GrandParent.js代码为例,如下
import React, {PureComponent} from "react"
import Parent from "./Parent"
class GrandParent extends PureComponent {
render() {
console.log("[👴🏼] [ ] [ ] [ ] rendered")
return (
<div>
<p>I'm a GrandParent Component</p>
<Parent />
<Parent />
</div>
)
}
}
export default GrandParent
所以现在的情况就是,只要我们点击"+1",所有的组件都会被依次渲染
为了优化这种一次渲染所有组件的情况,我们有三种方法。
shouldComponentUpdate()
根据形式也能判断出是class组件的生命周期方法,它的用法是接收将来的props和state与现在的props和state进行比较,如果return false,则表示不要更新;return true,则表示会更新重新render。
所以这里我们可以让GrandParent组件使用这个方法
因为GrandParent组件没有设置状态,所以使用props进行判断
shouldComponentUpdate(nextProps, nextState) {
if(nextProps.count === this.props.count) {
return false;
}
return true;
}
可以看到设置了props的count作为判断值,如果相等则返回false,不更新。
于是我们在App.js中传入这个count,count={this.props.count},此时每次点击GrandParent组件都会更新,因为每次传入的count都与上一次不同
那么为了使每次点击不刷新,我们可以直接删除传入的props,这样if始终成立,始终return false,也就是该组件不会更新,效果如下
React.PureComponent
PureComponent的用法相对shouldComponentUpdate来说更加简单,首先注释掉我们刚才写的代码
接下来我们第一步要做的就是将GrandParent组件中引入的Component改为PureComponent,如下
import React, {PureComponent} from "react"
import Parent from "./Parent"
class GrandParent extends PureComponent {
所以这时候重新运行,就会发现GrandParent组件已经不会随着App的渲染而重新渲染了。
那么如何让它能够重新渲染呢,答案也很简单,就是传入一个会改变的props,所以在这里就可以传入
<GrandParent count={this.state.count}/>,这样就获得了与shouldComponentUpdate类似的功能
React.memo()
在了解了前面两种方法后,这种方法也就非常容易理解了。
React.memo()是React官方写的一个高阶组件,它与PureComponent非常相像,只是一个用于class组件,而React.memo用于function组件,并且不对state进行判断。
所以它的用法如下,
import React from "react"
import Parent from "./Parent"
function GrandParent(props) {
console.log("[👴🏼] [ ] [ ] [ ] rendered")
return (
<div>
<p>I'm a GrandParent Component</p>
<Parent />
<Parent />
</div>
)
}
export default React.memo(GrandParent)
将组件化为函数组件,使用高阶组件进行加工后输出。此时点"+1"也不会再渲染GrandParent组件了
至于如何让GrandParent组件重新跟着渲染,那么答案也是一样的,在App.js中向GrandParent组件传入会随着改变的props即可
同理,如果是不想让Parent组件跟着渲染,只要对Parent.js进行上面三种方法的操作即可,以React.memo为例,代码如下
import React from "react"
import Child from "./Child"
function Parent(props){
console.log("[ ] [👩🏼⚕️] [ ] [ ] rendered")
return (
<div>
<p>I'm a Parent Component</p>
<Child />
<Child />
</div>
)
}
export default React.memo(Parent)
效果如下
小结
shouldComponentUpdate()、React.PureComponet以及React.memo()三种方法都能有效地防止组件被重复渲染,从而优化性能。在使用方面,shouldComponentUpdate的灵活性较高,但是大多数时候React.PureComponet已经可以满足使用需求。
所以class组件直接使用React.PureComponet,而function组件使用React.memo()即可。