起因
用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。
完结~