减轻state

减轻 state:只存储跟组件渲染相关的数据(比如:count / 列表数据 / loading 等)

注意:不用做渲染的数据不要放在state 中,比如定时器id等 
对于这种需要在多个方法中用到的数据,应该放在this 中
class Hello extends Component { 

componentDidMount() { // timerId存储到this中,而不是state中
this.timerId = setInterval(() => {}, 2000)
}

componentWillUnmount() {
clearInterval(this.timerId)
}
render() { … }

避免不必要的重新渲染

组件更新机制:父组件更新会引起子组件也被更新,这种思路很清晰

问题:子组件没有任何变化时也会重新渲染

如何避免不必要的重新渲染呢?

解决方式:使用钩子函数shouldComponentUpdate(nextProps, nextState)

作用:通过返回值决定该组件是否重新渲染,返回true 表示重新渲染,false 表示不重新渲染

触发时机:更新阶段的钩子函数,组件重新渲染前执行(shouldComponentUpdate —> render)

import React from 'react';
import ReactDOM from 'react-dom';

// 组件性能优化
class App extends React.Component {
state = {
count: 0
}
handleClick = () => {
this.setState(state => {return {count: state.count+1}})
}
shouldComponentUpdate(a,b) {
// 最新的状态
console.log("prop:",a,"state:",b) // a 和 b 是最新的值
// 更新前的状态
// 在这里面也可以获取 this.state 和 this.props, 这两个是旧的值
if(b.count<3) {
return true
}else {
return false
}
}
render() {
console.log("组件更新了")
return (
<div>
<h1>计数器:{this.state.count}</h1>
<button onClick={this.handleClick}>+1</button>
</div>
)
}
}
ReactDOM.render(<App />,document.getElementById('root'));

当新旧两个值相等的时候就不渲染,不相等的时候就渲染

class App extends React.Component {
state = {
count: 0
}
handleClick = () => {
this.setState(() => {return {count: Math.floor(Math.random()*3)}})
}
shouldComponentUpdate(a,b) {
console.log(b.count,this.state.count)
return b.count !== this.state.count; // 优化成了一句话
}
render() {
console.log("组件更新了")
return (
<div>
<h1>计数器:{this.state.count}</h1>
<button onClick={this.handleClick}>+1</button>
</div>
)
}
}
ReactDOM.render(<App />,document.getElementById('root'));

使用nextProps

import React from "react";
import ReactDOM from "react-dom";
// 组件性能优化
class App extends React.Component {
state = {
count: 0,
};
handleClick = () => {
this.setState(() => {
return { count: Math.floor(Math.random() * 3) };
});
};
render() {
return (
<div>
<Number number={this.state.count} />
<button onClick={this.handleClick}>+1</button>
</div>
);
}
}
class Number extends React.Component {
shouldComponentUpdate(a, b) {
console.log(a.number, this.props.number);
return a.number !== this.props.number;
}
render() {
console.log("子组件的render");
return <h1>计数器:{this.props.number}</h1>;
}
}
ReactDOM.render(<App />, document.getElementById("root"));

纯组件

纯组件:PureComponent 与 React.Component 功能相似

区别:==PureComponent 内部自动实现了shouldComponentUpdate 钩子,不需要手动比较 ==

原理:纯组件内部通过分别对比 前后两次 props 和 state 的值,来决定是否重新渲染组件

import React from "react";
import ReactDOM from "react-dom";
class App extends React.Component {
state = {
count: 0,
};
handleClick = () => {
this.setState(() => {
return { count: Math.floor(Math.random() * 3) };
});
};
render() {
return (
<div>
<Number number={this.state.count} />
<button onClick={this.handleClick}>+1</button>
</div>
);
}
}
// 纯组件
class Number extends React.PureComponent {
render() {
console.log("子组件的render");
return <h1>计数器:{this.props.number}</h1>;
}
ReactDOM.render(<App />, document.getElementById("root"));

纯组件----处理值类型和引用类型

说明:纯组件内部的对比是shallow compare(浅层对比)

对于值类型来说:比较两个值是否相同(直接赋值即可,没有坑)

let number = 0
let newNumber = number
newNumber = 2
console.log(number === newNumber) // false


state = { number: 0 }
setState({
number: Math.floor(Math.random() * 3)
})
// PureComponent内部对比:
最新的state.number === 上一次的state.number // false,重新渲染组件

说明:纯组件内部的对比是shallow compare(浅层对比)

对于引用类型来说:只比较对象的引用(地址)是否相同

const obj = { number: 0 }
const newObj = obj
newObj.number = 2
console.log(newObj === obj) // true


//错误demo
state = { obj: { number: 0 } }
state.obj.number = 2
setState({ obj: state.obj })
// PureComponent内部比较:
最新的state.obj === 上一次的state.obj // true,不重新渲染组件

错误demo,再怎么点击,render都不会渲染(这个时候其实count是在变的,就是不渲染),因为认为:newobj 和 this.state.obj是相等的,就不渲染了
最新的state.obj === 上一次的state.obj // true,不重新渲染组件

import React from "react";
import ReactDOM from "react-dom";

// 组件性能优化
class App extends React.PureComponent {
state = {
obj: {count: 0,}
};
handleClick = () => {
const newobj = this.state.obj
newobj.count = Math.floor(Math.random()*3)
this.setState(()=>{
return {
obj: newobj
}
})
}
render() {
return (
<div>
<h1>计数器:{this.state.obj.count}</h1>
<button onClick={this.handleClick}>+1</button>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));

注意:state 或 props 中属性值为引用类型时,应该创建新数据,不要直接修改原数据!(示例)

正确做法: 但是没有避免重复渲染的问题,你新创建一个,他们永不相等,那必然每次都渲染。

// 正确!创建新数据
const newObj = {...state.obj, number: 2}
setState({ obj: newObj })


// 正确!创建新数据
// 不要用数组的push / unshift 等直接修改当前数组的的方法
// 而应该用 concat 或 slice 等这些返回新数组的方法
this.setState({
list: [...this.state.list, {新数据}]
})
// 采用直接放在数组前面和后面,可以替代push和unshift

正确的demo:

import React from "react";
import ReactDOM from "react-dom";

// 组件性能优化
class App extends React.PureComponent {
state = {
obj: {count: 0,}
};
handleClick = () => {
const newobj = {...this.state.obj,count: Math.floor(Math.random()*3)}
this.setState(()=>{
return {
obj: newobj
}
})
}
render() {
console.log("更新")
return (
<div>
<h1>计数器:{this.state.obj.count}</h1>
<button onClick={this.handleClick}>+1</button>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));