写点什么

ReactNative 进阶(三十):Component、PureComponent 解析

  • 2022 年 1 月 23 日
  • 本文字数:1679 字

    阅读完需:约 6 分钟

ReactNative进阶(三十):Component、PureComponent 解析

一、前言

React.PureComponentReact.Component 几乎完全相同,但 React.PureComponent 通过propsstate的浅对比来实现 shouldComponentUpate()


PureComponent中,如果包含比较复杂的数据结构,可能会因深层的数据不一致而产生错误的否定判断,导致界面得不到更新。


如果定义了 shouldComponentUpdate(),无论组件是否是 PureComponent,它都会执行shouldComponentUpdate(),并根据结果来判断是否 update。如果组件未实现 shouldComponentUpdate() ,则会判断该组件是否是 PureComponent,如果是的话,会对新旧 propsstate 进行 shallowEqual 比较,一旦新旧不一致,会触发 update


浅对比:通过遍历对象上的键执行相等性,并在任何键具有参数之间不严格相等的值时返回 false。 当所有键的值严格相等时返回 true。

二、区别

PureComponent自带通过propsstate的浅对比来实现 shouldComponentUpate(),而Component没有。

2.1 PureComponent 缺点

可能会因深层的数据不一致而产生错误的否定判断,从而shouldComponentUpdate结果返回false,界面得不到更新。

2.2 PureComponent 优势

不需要开发者自己实现shouldComponentUpdate,就可以进行简单的判断来提升性能。

2.3 问题剖析

为什么PureComponent比较复杂的数据结构,可能会因深层的数据不一致而产生错误的否定判断?JavaScript 中的对象一般是可变的(Mutable),因为使用了引用赋值,新的对象简单的引用了原始对象,改变新的对象将影响到原始对象。如 foo={a: 1}; bar=foo; bar.a=2 你会发现此时 foo.a 也被改成了 2。


为了解决这个问题,一般的做法是使用 shallowCopy(浅拷贝)或 deepCopy(深拷贝)来避免被修改,但这样做造成了 CPU 和内存的浪费


接下来分析shallowEqual()函数


function shallowEqual(objA: mixed, objB: mixed): boolean {  // 首先对两个基本数据类型进行比较  if (is(objA, objB)) {    return true;  }
// 判断两个数据都为object的情况 if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) { return false; }
// 获得所有的key const keysA = Object.keys(objA); const keysB = Object.keys(objB);
// 判断两者key的数量是否一致 if (keysA.length !== keysB.length) { return false; }
// 如果key数量相同,使用一层for循环去比较 for (let i = 0; i < keysA.length; i++) { if ( // 判断对象B中是否包含对象A的key,即两者的keys是否一致 !hasOwnProperty.call(objB, keysA[i]) || // 通过is()函数对比A和B的key对应的数据 !is(objA[keysA[i]], objB[keysA[i]]) ) { return false; } }
复制代码


下面以组件的使用来举例:


例如:


class ChildComponent extends React.PureComponent {  render() {    return(      <div>        {this.props.numbers}      </div>    )  }}class MainComponent extends React.Component {  constructor(props) {    super(props);    this.handleClick = this.handleClick.bind(this);    this.state = {      numbers: [0]    }  }  handleClick() {    const arr = this.state.numbers;    arr.push(1);    this.setState({      numbers: arr    })    console.log(this.state.numbers)  }  render() {    <div>      <button onClick={this.handleClick} />      <ChildComponent numbers={this.state.numbers}/>    </div>  }}
复制代码


在 MainComponent 中去修改 numbers 时,ChildComponent 并没有得到刷新。原因在于 js 使用的是引用赋值,新的对象简单引用了原始对象,改变新对象虽然影响了原始对象,但对象的地址还是一样,使用===比较的方式相等。而在PureComponent中,会被判定prop相等而不触发render()


避免此类问题最简单的方式是,避免使用值可能会突变的属性或状态,而是使用副本来返回新的变量。


handleClick() {  this.setState(prevState => ({    numbers: [...prevState.numbers, 1],  }));};
复制代码

三、拓展阅读

发布于: 刚刚阅读数: 2
用户头像

No Silver Bullet 2021.07.09 加入

岂曰无衣 与子同袍

评论

发布
暂无评论
ReactNative进阶(三十):Component、PureComponent 解析