React.PureComponent
React.PureComponent 与 React.Component 很相似。
他们的区别在于,React.PureComponent 基于生命周期钩子函数 shouldComponentUpdate 进行了优化,它在 shouldComponentUpdate 中对 props 和 state 进行了浅比较,如果 props 和 state 没有发生变化,那么 React 组件则不会重新渲染。
接下来通过一个案例来深入认识 React.PureComponent:
// TaskItem.js
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import './style.css';
export default class TaskItem extends Component {
static propTypes = {
name: PropTypes.string.isRequired,
isFinish: PropTypes.bool.isRequired
}
render() {
return (
<li className={this.props.isFinish ? 'finish' : ''}>{this.props.name}</li>
);
}
}
// style.css
.finish {
color: 'green';
}// TaskList.js
import React, { Component } from 'react';
import TaskItem from './TaskItem.js';
import PropTypes from 'prop-types';
export default class TaskList extends Component {
static propTypes = {
tasks: PropTypes.arrayOf(PropTypes.shape({
name: PropTypes.string.isRequired,
isFinish: PropTypes.bool.isRequired
})).isRequired
}
render() {
const ts = this.props.tasks.map((item, index) => (<TaskItem {...item} key={index} />));
return (
<ul>
{ts}
</ul>
)
}
}// AddTask.js
import React, { Component } from "react";
import PropTypes from 'prop-types';
export default class AddTask extends Component {
state = {
value: "",
}
static propTypes = {
addTask: PropTypes.func.isRequired
}
handleChangeValue = e => {
this.setState({
value: e.target.value
});
}
handleAddTask = () => {
this.props.addTask &&
this.props.addTask({
name: this.state.value,
isFinish: false,
});
this.setState({
value: "",
});
}
render() {
console.log('AddTask render');
return (
<div>
<input value={this.state.value} onChange={this.handleChangeValue} />
<button onClick={this.handleAddTask}>添加 Task</button>
</div>
);
}
}// TaskContainer.js
import React, { Component } from 'react';
import TaskList from './TaskList.js';
export default class TaskContainer extends Component {
state = {
tasks: []
}
componentDidMount() {
let tasks = [];
for(let i = 0; i < 10; i++) {
tasks.push({
name: `任务${i + 1}`,
isFinish: Math.random() > 0.5
});
}
this.setState({ tasks });
}
render() {
console.log('TaskContainer render');
return (
<>
<TaskList tasks={this.state.tasks} />
</>
);
}
}控制台输出如下:

这里输出其实就能看出问题, TaskContainer 组件在第二次渲染时,并没有渲染 AddTask 组件的必要,因为它并没有发生改变。
我们添加一个新任务之后,再看一下控制台输出:

AddTask 组件重新渲染,这是因为我们将 AddTask 组件里的状态 value 置空导致的。
真正的问题发生在我们只添加了一个新任务,前面旧的10个 TaskItem 组件也跟着一起重新渲染了,虽然他们并没有发生变化。
这个问题,可以通过 shouldComponentUpdate 来优化,当组件的 props 和 state 没有变化时,则不需要重新渲染。
- 新增一个工具方法,用于判断两个对象是否相等(浅比较)
// utils.js
export function ObjectEqual(obj1, obj2) {
for(let prop in obj1) {
if(!Object.is(obj1[prop], obj2[prop])) {
return false
}
}
return true;
}- 修改 TaskItem 组件
// TaskItem.js
import React, { Component } from "react";
import PropTypes from "prop-types";
import "./TaskItem.css";
import { ObjectEqual } from "../utils";
export default class TaskItem extends Component {
static propTypes = {
name: PropTypes.string.isRequired,
isFinish: PropTypes.bool.isRequired,
};
shouldComponentUpdate(nextProps, nextState) {
var isPropsSame = ObjectEqual(this.props, nextProps);
var isStateSame = ObjectEqual(this.state, nextState);
if (isPropsSame && isStateSame) {
return false;
}
return true;
}
render() {
console.log("TaskItem render");
return (
<li className={this.props.isFinish ? "finish" : ""}>{this.props.name}</li>
);
}
}重新添加一次新任务,看一下控制台输出:

可以看到,添加一次新任务后,TaskItem 组件只渲染了一次。
而我们通过 shouldComponentUpdate 做的优化,也可以使用 React.PureComponent 来代替,效果是一样的。
// TaskItem.js
import React, { PureComponent } from "react";
import PropTypes from "prop-types";
import "./TaskItem.css";
// import { ObjectEqual } from "../utils";
export default class TaskItem extends PureComponent {
static propTypes = {
name: PropTypes.string.isRequired,
isFinish: PropTypes.bool.isRequired,
};
// shouldComponentUpdate(nextProps, nextState) {
// var isPropsSame = ObjectEqual(this.props, nextProps);
// var isStateSame = ObjectEqual(this.state, nextState);
// if (isPropsSame && isStateSame) {
// return false;
// }
// return true;
// }
render() {
console.log("TaskItem render");
return (
<li className={this.props.isFinish ? "finish" : ""}>{this.props.name}</li>
);
}
}AddTask 组件在 TaskContainer 组件第二次渲染时,也跟着重新渲染的问题,同样可以通过 React.PureComponent 来优化。
因此,在平时的开发中,我们可以尽量使用 React.PureComponent 来提高效率,并且需要注意,在修改状态时,永远是创建新状态去覆盖旧的状态。