memo可以解决react渲染时的效率问题,react可以将数据渲染在视图中,如果数据没有变化,视图还是重新渲染
import React, { Component } from 'react';
// , PureComponent, memo, useState, useMemo, useCallback
class Foo extends Component {
render() {
console.log('Foo reander');
return null;
}
}
class App extends Component {
state = {
count: 0
};
render() {
return (
<div>
<p>{this.state.count}</p>
<button
onClick={() => { this.setState({ count: this.state.count + 1 }) }}>
Add
</button>
<Foo name="happychen" />
</div>
)
}
}
export default App;
效果:
发现每次点击Foo子组件就会被重新渲染,即使它里面的数据没有发生变化不需要重新渲染,这就可以优化。react中有个生命周期函数shouldComponentUpdate
可以控制组件是否重新渲染。
import React, { Component } from 'react';
// , PureComponent, memo, useState, useMemo, useCallback
import './App.css';
class Foo extends Component {
render() {
console.log('Foo reander');
return null;
}
}
class App extends Component {
state = {
count: 0
};
//nextProps是下次渲染用到的props
shouldComponentUpdate(nextProps, nextState) {
//将下次渲染用到的props和当前props对比
if (nextProps.name === this.props.name) {
return false;//返回false则当前组件不会被重新渲染
}
return true;//返回true则当前组件需要被重新渲染
}
render() {
return (
<div className="app">
<p>{this.state.count}</p>
<button
onClick={() => { this.setState({ count: this.state.count + 1 }) }}>
Add
</button>
<Foo name="happychen" />
</div>
)
}
}
export default App;
效果:
虽然App组件重新渲染了,但在它的子组件Foo中加上shouldComponentUpdate
生命周期函数后,Foo组件可以通过name前后值是否发生变化来判断是否重新渲染。
也可以通过react中的PureComponent
组件达到同样的效果。
import React, { Component, PureComponent } from 'react';
// , PureComponent, memo, useState, useMemo, useCallback
import './App.css';
class Foo extends PureComponent {
render() {
console.log('Foo reander');
return null;
}
}
class App extends Component {
state = {
count: 0
};
render() {
return (
<div className="app">
<p>{this.state.count}</p>
<button
onClick={() => { this.setState({ count: this.state.count + 1 }) }}>
Add
</button>
<Foo name="happychen" />
</div>
)
}
}
export default App;
但是PureComponent
的实现和使用场景是有局限性的,比如在state
中定义一个引用类型的状态。
import React, { Component, PureComponent } from 'react';
// , PureComponent, memo, useState, useMemo, useCallback
import './App.css';
class Foo extends PureComponent {
render() {
console.log('Foo reander');
return <div>子组件person.age的值:{this.props.person.age}</div>;
}
}
class App extends Component {
state = {
count: 0,
person: {
age: 18
}
};
render() {
const person = this.state.person;
return (
<div className="app">
<p>父组件person.age的值:{person.age}</p>
<Foo person={person} />
<button
onClick={() => {
person.age++;
this.setState({
person
})
}}>
Add
</button>
</div>
)
}
}
export default App;
效果:
此时Foo组件本应该重新渲染但并没有重新渲染,是因为PureComponent
提供的shouldComponentUpdate
发现person的句柄本身没有发生变化才拒绝重新渲染。只有传入的props第一级发生变化才会触发重新渲染。
另外一个陷阱是:下面不改变person属性的值,而是向Foo组件传入一个回调函数。
import React, { Component, PureComponent } from 'react';
// , PureComponent, memo, useState, useMemo, useCallback
import './App.css';
class Foo extends PureComponent {
render() {
console.log('Foo reander');
return <div>子组件person.age的值:{this.props.person.age}</div>;
}
}
class App extends Component {
state = {
count: 0,
person: {
age: 18
}
};
render() {
const person = this.state.person;
return (
<div className="app">
<p>父组件person.age的值:{person.age}</p>
<Foo person={person} cb={() => { }} />
<button
onClick={() => {
this.setState({
count: this.state.count + 1
})
}}>
Add
</button>
</div>
)
}
}
export default App;
效果:
这是因为虽然person本身没有发生变化,但每次父组件重新渲染时 传入子组件的内联函数句柄会发生变化,所以Foo组件依然每次重新渲染。
换种回调函数写法:
import React, { Component, PureComponent } from 'react';
// , PureComponent, memo, useState, useMemo, useCallback
import './App.css';
class Foo extends PureComponent {
render() {
console.log('Foo reander');
return <div>子组件person.age的值:{this.props.person.age}</div>;
}
}
class App extends Component {
state = {
count: 0,
person: {
age: 18
}
};
callback(){}
render() {
const person = this.state.person;
return (
<div className="app">
<p>父组件person.age的值:{person.age}</p>
<Foo person={person} cb={this.callback} />
<button
onClick={() => {
this.setState({
count: this.state.count + 1
})
}}>
Add
</button>
</div>
)
}
}
export default App;
效果:
Foo组件没有重新渲染。但这样导致的后果是callback函数中的this指向会发生变化。
接着修改this的指向
,这样依然会创建新的函数对象,修改完this的指向后Foo组件还是会每次重新渲染。
接着改:将callback改成类属性
import React, { Component, PureComponent } from 'react';
// , PureComponent, memo, useState, useMemo, useCallback
import './App.css';
class Foo extends PureComponent {
render() {
console.log('Foo reander');
return <div>子组件person.age的值:{this.props.person.age}</div>;
}
}
class App extends Component {
state = {
count: 0,
person: {
age: 18
}
};
callback = () => { }
render() {
const person = this.state.person;
return (
<div className="app">
<p>count值:{this.state.count}</p>
<Foo person={person} cb={this.callback} />
<button
onClick={() => {
this.setState({
count: this.state.count + 1
})
}}>
Add
</button>
</div>
)
}
}
export default App;
这样利用箭头函数不会修改this指向,运行发现Foo组件不会每次重新渲染,这样就相对完美了,这是最常见的写法。
将Foo组件改写成函数组件:
import React, { Component, PureComponent } from 'react';
// , PureComponent, memo, useState, useMemo, useCallback
import './App.css';
function Foo(props){
console.log('Foo reander');
return <div>子组件person.age的值:{props.person.age}</div>;
}
class App extends Component{
state = {
count: 0,
person: {
age: 18
}
};
callback = () => { }
render() {
const person = this.state.person;
return (
<div className="app">
<p>count值:{this.state.count}</p>
<Foo person={person} cb={this.callback} />
<button
onClick={() => {
this.setState({
count: this.state.count + 1
})
}}>
Add
</button>
</div>
)
}
}
export default App;
效果:
此时PureComponent
组件的特性没有用到了,Foo组件会被重新渲染。接着将函数Foo组件用react的memo包裹起来返回新的组件:
import React, { Component,memo } from 'react';
// , PureComponent, memo, useState, useMemo, useCallback
import './App.css';
const Foo = memo(function Foo(props){
console.log('Foo reander');
return <div>子组件person.age的值:{props.person.age}</div>;
})
class App extends Component{
state = {
count: 0,
person: {
age: 18
}
};
callback = () => { }
render() {
const person = this.state.person;
return (
<div className="app">
<p>count值:{this.state.count}</p>
<Foo person={person} cb={this.callback} />
<button
onClick={() => {
this.setState({
count: this.state.count + 1
})
}}>
Add
</button>
</div>
)
}
}
export default App;
效果:
将Foo组件用react的memo包裹起来,App父组件发生变化时Foo组件没有被重新渲染。
PureComponent
为组件提供了对比算法来避免组件做无意义的渲染,减少性能开销。而无状态组件是函数式的,不能继承PureComponent
,但可以用memo
包裹函数式组件达到同样的效果。