react之PureComponent下setstate不触发render

2,555 阅读2分钟

在使用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。