在使用React.PureComponent组件时,会遇到明明改变了值, 并且回调函数也触发了, 但是就是不触发render
import React, { PureComponent } from 'react';
export default class example extends PureComponent{
constructor () {
super();
this.state = {
arr: [1, 3, 5],
}
}
handleClick = () => {
const { arr } = this.state;
arr.forEach(item => {
arr.push(item * 2);
});
this.setState(
{
arr,
},
() => {
console.log('trigger callback');
},
);
};
render(){
const { arr } = this.state;
return (
<div>
{arr.map(item => (
<p key={item}>{item}</p>
))}
<button onClick={this.handleClick}>Click</button>
</div>
)
}
}
点击过后,发现只打印了trigger callback,而组件没有任何变化。
解决方法:
this.setState(
{
arr: [...arr],
},
() => {
console.log('trigger callback');
},
);
但是用Component代替PureComponent 后,使用 this.setState({ arr })
这种写法又是ok的。
这里涉及到Component与PureComponent的不同点;以及 深克隆与浅克隆。
深克隆与浅克隆
spreed 扩展运算... 相对于原始值是深克隆
let obj = { a: 1, b: 2 };
let newObj = { ...obj }
newObj.a = 3;
console.log(obj); // {a: 1, b: 2}
console.log(newObj); // {a: 3, b: 2}
但是,如果对象某个属性的值是引用值,那还是浅克隆
let obj = {
a: {
b: 1,
}
};
let newObj = {...obj};
newObj.a.b = 2;
console.log(obj.a.b); // 2
如果是上诉这种情况,可以使用JSON.parse(JSON.stringify(obj))
JSON.parse(JSON.stringify(obj))
对象的解构赋值是浅克隆,
let obj = {
a: {
b: 1
},
};
let { a } = obj;
a.b = 2;
console.log(obj.a.b); // 2
PureComponent与Component
当组件更新时,如果组件的 props 和 state 都没发生改变,render 方法就不会触发,省去 Virtual DOM 的生成和比对过程,达到提升性能的目的。
在React Component的生命周期中,有一个shouldComponentUpdate方法。这个方法默认返回值是true。
Component不会比较当前和下个状态的props和state,因此,每当shouldComponentUpdate被调用时,组件默认的会重新渲染。
PureComponent有默认的shouldComponentUpdate行为。会一一比较props和state中所有的属性,只有当其中任意一项发生改变时,才会进行重绘。 但是需要特别注意的,PureComponent使用浅比较判断组件是否需要重新渲染(内存地址是否一致,如果一致则不更新)。
总结
Component只要有setState就会重新渲染,而PureComponent需要先进行浅比较才决定是否重绘。 因此,如果在对性能要求没有非常强烈的情况下,可以只用Component。