React中PureComponent原理

PureComponent 会对 propsstate 进行浅层比较,并减少了跳过必要更新的可能性。

React.PureComponentReact.Component 很相似。

两者的区别在于 React.Component 并未实现 shouldComponentUpdate(),而 React.PureComponent中以浅层对比 propstate 的方式来实现了该函数。

如果赋予 React 组件相同的 propsstaterender() 函数会渲染相同的内容,那么在某些情况下使用 React.PureComponent 可提高性能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import React from 'react';
import ReactDOM from 'react-dom';

class Counter extends React.Component {
state = {
count: 0
}
handleClick = () => {
this.setState({
count: this.state.count + 1
})
}
render() {
return (
<div>
<Title title='计数器'></Title>
<span>
{ this.state.count }
</span>
<button
onClick={this.handleClick}>
+1
</button>
</div>
);
}
}

class Title extends React.Component {
render() {
// 检查是否多次打印
console.log('Title render')
return (
<p>{this.props.title}</p>
);
}
}

ReactDOM.render(<Counter />, document.getElementById('root'))

上面演示的代码中 Counter 组件中使用了 Title 组件,Counter 组件每次修改状态都会导致 Title 组件重新渲染。可以看到 console 语句多次打印出 Title render。如果不想让 Title 组件多次渲染,就需要让 Title 组件继承 PureComponent 组件。

开始使用

  1. 类组件使用 PureComponent
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import React from 'react';
import ReactDOM from 'react-dom';

class Counter extends React.Component {
state = {
count: 0
}
handleClick = () => {
this.setState({ count: this.state.count + 1 })
}
render() {
return (
<div>
<Title title='计数器'></Title>
<span>
{ this.state.count }
</span>
<button
onClick={this.handleClick}>
+1
</button>
</div>
);
}
}

class Title extends React.PureComponent {
render() {
console.log('Title render')
return (
<p>
{ this.props.title }
</p>
);
}
}

ReactDOM.render(<Counter />, document.getElementById('root'))
  1. 函数组件使用 memo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Counter extends React.Component {
state = {
count: 0
}
handleClick = () => {
this.setState({
count: this.state.count + 1
})
}
render() {
return (
<div>
<Title title='计数器'></Title>
<span>
{ this.state.count }
</span>
<button
onClick={this.handleClick}>
+1
</button>
</div>
);
}
}
const Title = React.memo(props => {
console.log('Title render')
return <p>{props.title}</p>
})
  1. memo 的原理
1
2
3
4
5
6
7
8
9
10
function memo (Func) {
class Proxy extends React.PureComponent {
render() {
return (
<Func {...this.props} />
);
}
}
return Proxy
}
  1. PureComponent 的原理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import React, { Component } from 'react';

function shallowEqual(obj1, obj2) {
if (obj1 === obj2) {
return true
}
let noObj1 = typeof obj1 !== 'object' || obj1 === null
let noObj2 = typeof obj2 !== 'object' || obj2 === null
if (noObj1 || noObj2) {
return false
}
let keys1 = Object.keys(obj1)
let keys2 = Object.keys(obj2)
if (keys1.length !== keys2.length) {
return false
}
for (const key of keys1) {
let equalKey = obj1[key] === obj2[key]
if (!obj2.hasOwnProperty(key) || !equalKey) {
return false
}
}
return true
}

class PureComponent extends Component {
shouldComponentUpdate(nextProps, nextState) {
let equalProps = shallowEqual(this.props, nextProps)
let equalState = shallowEqual(this.state, nextState)
return !equalProps || !equalState
}
render() {
return this.props.children;
}
}

export default PureComponent;