主要知识点
- shouldComponentUpdate(简称SCU)
- PureComponent和React.memo
- 不可变值immutable.js
SCU
基本用法
shouldComponentUpdate(nextProps,nextState) {
if(nextState.count !== this.state.count){
return true //可以渲染
}
return false // 不重复渲染
}
思考:为什么react定义了这样的生命周期,默认返回true,还给你一个定制的权利?
举例一:
先来看一段代码
import React from "react";
import PropTypes from "prop-types";
// input组件
class Input extends React.Component {
constructor(props) {
super(props);
this.state = {
title: "",
};
}
render() {
return (
<div>
<input value={this.state.title} onChange={this.onTitleChange} />
<button onClick={this.onSubmit}>提交</button>
</div>
);
}
onTitleChange = (e) => {
this.setState({
title: e.target.value,
});
};
onSubmit = () => {
const { submitTitle } = this.props;
submitTitle(this.state.title); // 'abc'
this.setState({
title: "",
});
};
}
// props 类型检查
Input.propTypes = {
submitTitle: PropTypes.func.isRequired,
};
// list组件
class List extends React.Component {
constructor(props) {
super(props);
}
render() {
const { list } = this.props;
return (
<ul>
{list.map((item, index) => {
return (
<li key={item.id}>
<span>{item.title}</span>
</li>
);
})}
</ul>
);
}
}
// props 类型检查
List.propTypes = {
list: PropTypes.arrayOf(PropTypes.object).isRequired,
};
// footer组件
class Footer extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<p>
{this.props.text}
{this.props.length}
</p>
);
}
componentDidUpdate() {
console.log("footer did update");
}
shouldComponentUpdate(nextProps, nextState) {
if (nextProps.text !== this.props.text) {
return true;
}
return false;
}
}
// 顶级组件
class TodoListDemo extends React.Component {
constructor(props) {
super(props);
// 状态(数据)提升
this.state = {
list: [
{
id: "id-1",
title: "标题1",
},
{
id: "id-2",
title: "标题2",
},
{
id: "id-3",
title: "标题3",
},
],
footerInfo: "底部文字",
};
}
render() {
return (
<div>
<Input submitTitle={this.onSubmitTitle} />
<List list={this.state.list} />
<Footer text={this.state.footerInfo} length={this.state.list.length} />
</div>
);
}
onSubmitTitle = (title) => {
this.setState({
list: this.state.list.concat({
id: `id-${Date.now()}`,
title,
}),
});
};
}
export default TodoListDemo;
我们输入一些值来看一下效果。
可以发现我们的footer组件,每一次提交都要更新一次。假如我们有这样一个场景,就是Footer的数据量很大,而且List和Input的组件值不会影响到我们的Footer组件,不需要进行每一次重新渲染,这时那么我们的shouldComponentUpdate就派上了用场了。
我们加上了这一段代码后,发现footer组件并没有随着其他组件的更新而重新渲染。
举例二:
还是上面的代码结构,我在TodoListDemo和List做了一些改造。
TodoListDemo代码:
注意看这里,我们先是做了一个push的操作,然后在进行赋值
List代码:
效果:
我依旧输入了三次的值,但是发现列表并没有添加值,而且并没有重新渲染。这是为什么呢?
事实上用push方法更新数据时,传递的nextProps.list和this.props.list的值是相等的,所以没有进行重新渲染,不遵守不可变值的规范,所以会出现bug。
正确的写法使用slice或者concat。
注意:
添加 shouldComponentUpdate
方法时,不建议使用深度相等检查(如使用 JSON.stringify()
),因为深比较效率很低,可能会比重新渲染组件效率还低。而且该方法维护比较困难,建议使用该方法会产生明显的性能提升时使用。
小结:
- React 默认:父组件有更新,子组件则无条件也更新!!!
- 性能优化对于 React 更加重要!
- 必须配合”不可变值”一起使用
- SCU 一定要每次都用吗?—— 需要的时候才优化
PureComponent
定制了shouldComponentUpdate后的Component React.PureComponent
与 React.Component
很相似。两者的区别在于 React.Component
并未实现 shouldComponentUpdate()
,而 React.PureComponent
中以浅层对比 props 和 state 的方式来实现了该函数。
如果赋予 React 组件相同的 props 和 state,render()
函数会渲染相同的内容,那么在某些情况下使用 React.PureComponent
可提高性能。
- PureComponent,SCU中实现了浅比较
- 浅比较已使用大部分情况(尽量不要做深比较)
使用注意,要进行值比较,使用引用类型时确保地址不改变
import React, { PureComponent } from "react";
export default class PuerComponentPage extends PureComponent {
constructor(props) {
super(props);
this.state = {
counter: 0,
obj: { num: 100 },
};
}
setCounter = () => {
this.setState({ counter: 1, obj: { num: 200 } });
console.log("setCounter");
};
render() {
console.log("render");
const { counter, obj } = this.state;
return (
<div>
<button onClick={this.setCounter}>setCounter</button>{" "}
<div>counter: {counter}</div>
<div>obj.num: {obj.num}</div>
</div>
);
}
}
缺点是必须要用class形式,而且要注意是浅⽐较
React.memo
React.memo(…) 是React v16.6引进来的新属性。它的作⽤和 React.PureComponent 类似,是⽤来控制函数组件的重新渲染的。 React.memo(…) 其实就是函数组件的 React.PureComponent。
import React, { Component, memo } from "react";
export default class MemoPage extends Component {
constructor(props) {
super(props);
this.state = {
counter: 0,
obj: { num: -1 },
};
}
setCounter = () => {
this.setState({ counter: 1 /* , obj: {
num: 100,
}, */ });
};
render() {
const { counter } = this.state;
return (
<div>
<h1>MemoPage</h1>
<button onClick={this.setCounter}>按钮</button>
{/* <PuerCounter counter={counter} obj={obj} /> */} // 浅比较,使用引用类型失去作用
<PuerCounter counter={counter} />
</div>
);
}
}
const PuerCounter = memo((props) => {
console.log("render");
return <div>{props.counter}</div>;
});
Immutable.js
- 彻底拥抱不可变值
- 基于共享数据(不是深拷贝),速度好
- 有一定学习和迁移成本,按需求使用