起因

用React.PureComponent时,更新state外面的count用的形式是++this.state.count,然而意外的导致组件没有从新render(自己用Hook组件较多,所以感到很纳闷)

import React from 'react';import { Button } from 'antd-mobile';class DemoChildClass extends React.PureComponent {  constructor(props) {    super(props);    this.state = {      num: 1    }  }  addNum = () => {    this.setState({      num: ++this.state.num    }, () => {      console.log('fffgggghhhh1111', this.state.num)    })    console.log('fffgggghhhh222', this.state.num)  }  render() {    const { num } = this.state;    return (      <div className='demo-child'>        这是儿子,这个数字就是{num},hhhh< br />        <Button onClick={this.addNum}>子++++</Button>      </div>    )  }}export default DemoChildClass;

打印后果如下:

一开始是没有加两个console.log的,所以依照惯识,认为代码没问题,逻辑没问题,然而预期是谬误的。

于是,更改一下setstate形式,如下:

addNum = () => {    let { num } = this.state;    this.setState({      num: ++num    })  }

发现他喵的render了?WHF???为什么?

开始时,认为是PureComponent的问题,外部做了优化,导致渲染更新跟预期不统一,于是去看了PureComponent的源码。

PureComponent

PureComponent并不是react与生俱来就有的,而应该是在15.3版本之后才呈现的,次要是为了取代之前的PureRenderMixin。

PureComponent其实就是一个继承自Component的子类,会主动加载shouldComponentUpdate函数。当组件须要更新的时候,shouldComponentUpdate会对组件的props和state进行一次浅比拟。如果props和state都没有发生变化,那么render办法也就不会触发,当然也就省去了之后的虚构dom的生成和比照,在react性能方面失去了优化。

PureComponent源码:

export defualut function PureComponent (props, context) {    Component.call(this, props, context)}PureComponent.prototype = Object.create(Component.prototye)PureComponent.prototype.contructor = PureComponentPureComponent.prototype.shouldComponentUpdate = shallowCompare function shallowCompare (nextProps, nextState) {    return !shallowEqual(this.props, nextProps) || !shallowEqual(this.state, nextState)}

咱们能够看到PrueComponent总体来说就是继承了Component,只不过是将shouldComponentUpdate重写成了shallowCompare。而在shallowCompare中只是返回了shallowEqual的返回值。

shallowEqual的源码:

function shallowEqual(objA: mixed, objB: mixed): boolean {  if (is(objA, objB)) {    return true;  }   if (typeof objA !== 'object' || objA === null ||      typeof objB !== 'object' || objB === null) {    return false;  }   const keysA = Object.keys(objA);  const keysB = Object.keys(objB);   if (keysA.length !== keysB.length) {    return false;  }   // Test for A's keys different from B.  for (let i = 0; i < keysA.length; i++) {    if (      !hasOwnProperty.call(objB, keysA[i]) ||      !is(objA[keysA[i]], objB[keysA[i]])    ) {      return false;    }  }   return true;}

所以从shallowEqual中能够看出,其实就是比拟了传入的对象是不是统一,也就是浅比拟了,props和state是不是一样。从而来实现了一个另类的shouldComponentUpdate函数。所以从源码来,PureCompoennt仅仅是一个props和state的浅比拟。当props和state是对象的时候,并不能阻止不必要的渲染。

比拟程序?
step1:先判断两个对象是否地址雷同。如果地址雷同,就间接返回true;如果地址不雷同,就持续判断(根本类型的相等性判断);
step2:再判断两个props的key数量,是否雷同,如果雷同就持续下一步的判断;如果不雷同,就间接返回false;
step3:最初一步,别离判断每个key对应的value值,是否雷同。判断value是否雷同,应用的是object.is()。

附PureComponent和Component的区别是什么?

1、就像是下面介绍PureComponent一样,和Component的一个最大的区别在于PureComponent会主动执行shouldComponentUpdate函数,通过shallowEqual的浅比照,实现react的性能优化。而Component必须要通过本人去调用生命周期函数shouldComponentUpdate来实现react组件的优化;2、PureComponent不仅会影响自身,而且会影响子组件,所以PureComponent最佳状况是展现组件(1)退出父组件是继承自PureComponent,而子组件是继承自Component,那么如果当父组件的props或者是state没有变动而子组件的props或者state有变动,那么此时子组件也不会有更新,因为子组件受到父组件的印象,父组件没有更新。(2)如果,父子组件均继承自PureComponent,那么父子组件的更新就会依赖与各自的props和state。(3)父组件是继承自Component,而子组件是继承自PureComponent那么就是看各自的props和state。(4)当然如果父子组件都是继承自Component那么就是只有有更新,那么都会去从新渲染。3、若是数组和对象等援用类型,则要援用不同,才会渲染;4、如果prop和state每次都会变,那么PureComponent的效率还不如Component,因为你晓得的,进行浅比拟也是须要工夫;5、若有shouldComponentUpdate,则执行它,若没有这个办法会判断是不是PureComponent,若是,进行浅比拟。

下面一段属于扩大了,咱们回到正题,既然PureComponent进行了浅比拟,那就阐明preState的值和nextState的值是一样的,所以没有进行render,通过打印咱们也能够看到:

那到底是为什么呢?
最终,咱们在一位博客大佬的解释下晓得了:

this.setState({  num: ++this.state.num})就相当于上面这段代码:const newNum = ++this.state.num;this.setState({  num: newNum})

解释一下,【++this.state.num】,去更改了state的值(也更改了preState),然而没有用setState去更新,所以没有render。而后再进行setState,这时PureComponent会进行浅比拟,preState和nextState是一样的,所以还是没有进行render。

也就是说在PureComponent组件中,如果先进行数据更新,而后再进行setState的话,都会是一样的后果,不会render。然鹅,在一般Component组件中,这些问题将不再存在。

所以,在PureComponent组件中,更新state最好的形式是更改state的援用地址,来触发render,然而如果都是采纳这种形式的话,就不要用PureComponent了,间接用Component吧!!

最初再来解释一下上面这个写法为啥没问题:

addNum = () => {    let { num } = this.state;    this.setState({      num: ++num    })  }

解构或者从新定义一个值去承受num,相当于在栈内存中从新开拓了一块内存空间变量叫num2,而后进行++num2,改的只是num2,state中num还是原来的值,所以在进行浅比拟时,preState和nextState是不一样的,所以会从新render。

完结~