专栏首页TechBoxReact Native之PureComponent

React Native之PureComponent

前言

PureComponent继承自Component。PureComponent几乎和Component完全相同。

唯一不同之处在于Component的shouldComponentUpdate的默认实现是返回true。而PureComponent通过props和state的浅比较实现shouldComponentUpdate,某些情况下使用PureComponent可以减少不必要的渲染,提升性能。

PureComponent原理

之所以,Components的props和state每次发生变化时都会触发render。是因为Component的shouldComponentUpdate方法默认返回了true。而PureComponent的shouldComponentUpdate并非简单的返回了true。

当PureComponent组件更新时,如果组件的 props 和 state 都没发生改变, render 方法就不会触发,省去 Virtual DOM 的生成和比对过程,达到提升性能的目的。具体就是 React 自动帮我们做了一层浅比较shallowEqual(即指针比较),如下是PureComponent的shouldComponentUpdate的源码:

if (this._compositeType === CompositeTypes.PureClass) {
  shouldUpdate = !shallowEqual(prevProps, nextProps)
  || !shallowEqual(inst.state, nextState);
}

关于浅比较的更多内容可以参考https://www.imweb.io/topic/598973c2c72aa8db35d2e291

当把之前和下一个的props和state作比较,浅比较将检查原始值是否有相同的值(例如:1 == 1或者ture==true);而对于数组和对象类型,将会比较引用是否相同。

PureComponent注意事项

1.可变数据变化前后不能使用同一个引用。 2.不变数据变化前后需使用同一个引用。

具体解释

1> 使用PureComponent不要在props和state中改变对象和数组这种引用类型。即可变数据不能使用同一个引用。如果你在你的父组件中改变对象,你的“pure”子组件不将更新。虽然值已经被改变,但是子组件比较的是之前props的引用是否相同,所以不会检测到不同。因此,你可以通过使用es6的assign方法或者数组的扩展运算符或者使用第三方库,强制返回一个新的对象。

2> 不要在render的函数中绑定值。即不变数据变化前后需使用同一个引用。

假设你有一个项目列表,每个项目都传递一个唯一的参数到父方法。为了绑定参数,你可能会这么做:

<CommentItem likeComment={() => this.likeComment(user.id)} />

这个问题会导致每次父组件render方法被调用时,一个新的函数被创建,已将其传入likeComment。这会有一个改变每个子组件props的副作用,它将会造成他们全部重新渲染,即使数据本身没有发生变化。

为了解决这个问题,只需要将父组件的原型方法的引用传递给子组件。子组件的likeComment属性将总是有相同的引用,这样就不会造成不必要的重新渲染。

<CommentItem likeComment={this.likeComment} userID={user.id} />

然后再子组件中创建一个引用了传入属性的类方法:

class CommentItem extends PureComponent { ... handleLike() { this.props.likeComment(this.props.userID) } ... }

3> 不要在render方法里派生数据。即不变数据变化前后使用需同一个引用。

考虑一下你的配置组件将从一系列文章中展示用户最喜欢的十篇文章。

render() { const { posts } = this.props const topTen = posts.sort((a, b) => b.likes - a.likes).slice(0, 9) return //... }

每次组件重新渲染时topTen都将有一个新的引用,即使posts没有改变并且派生数据也是相同的。这将造成列表不必要的重新渲染。

你可以通过缓存你的派生数据来解决这个问题。例如,设置派生数据在你的组件state中,仅当posts更新时它才更新。

componentWillMount() { this.setTopTenPosts(this.props.posts) } componentWillReceiveProps(nextProps) { if (this.props.posts !== nextProps.posts) { this.setTopTenPosts(nextProps) } } setTopTenPosts(posts) { this.setState({ topTen: posts.sort((a, b) => b.likes - a.likes).slice(0, 9) }) }

如果你正在使用Redux,可以考虑使用reselect来创建"selectors"来组合和缓存派生数据。

4> 子组件数据更新,使用Immutable.js库解决数据不变问题。

有时候数组或对象内部依旧持有的是数组或对象,数据引用变化,虽然指针变了,但是内层数据实际上没变化,此时也会触发render。这个时候可以使用immutable-js函数库。

5> 复杂状态与简单状态不要共用一个组件

这点可能和 PureComponent 没多少关系,但做的不好可能会浪费很多性能,比如一个页面上面一部分是一个复杂的列表,下面是一个输入框,抽象代码:

change = (e) => {
  this.setState({ value: e.target.value });
}
render() {
  return (<div>
    <ul>
      {this.state.items.map((i, k) => <li key={k}> {...}</li>)}
    </ul>
    <input value={this.state.value} onChange={this.change} />
  </div>)
}

表单和列表其实是没有什么关联的,表单的值也可能经常变动,但它的会给列表也带来必然的 diff操作,这是没必要的,最好是给列表抽出成一个单独的 PureComponent 组件,这样 state.items不变的话,列表就不会重新 render 了。

PureComponent与shouldComponentUpdate共存

如果 PureComponent 里有 shouldComponentUpdate 函数的话,直接使用 shouldComponentUpdate 的结果作为是否更新的依据,没有 shouldComponentUpdate 函数的话,才会去判断是不是 PureComponent ,是的话再去做 shallowEqual 浅比较。

原则

  1. 虽然通常情况下易变性就是不好的,但是当使用PureComponent时问题会变得复杂。尽量让数据不可变,可以使用Immutable.js。
  2. 如果你在render方法中创建一个新的函数、对象或者是数组,那么你的做法(可能)是错误的。
  3. PureComponent不仅会影响本身,而且会影响子组件,所以PureComponent最佳情况是展示组件。(关于展示组件可以参考https://juejin.im/post/5a52fe32f265da3e317e008bhttps://juejin.im/post/5af3d66ef265da0b7b35ec1d

浅比较

shallowEqual会比较 Object.keys(state | props) 的长度是否一致,每一个 key 是否两者都有,并且是否是一个引用,也就是只比较了第一层的值,确实很浅,所以深层的嵌套数据是对比不出来的。

shallowEqual源码

const hasOwn = Object.prototype.hasOwnProperty

function is(x, y) {
  if (x === y) {
    return x !== 0 || y !== 0 || 1 / x === 1 / y
  } else {
    return x !== x && y !== y
  }
}

export default function shallowEqual(objA, objB) {
  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

  for (let i = 0; i < keysA.length; i++) {
    if (!hasOwn.call(objB, keysA[i]) ||
        !is(objA[keysA[i]], objB[keysA[i]])) {
      return false
    }
  }

  return true
}

参考

PureComponent

https://segmentfault.com/a/1190000014979065 https://wulv.site/2017-05-31/react-purecomponent.html https://juejin.im/post/5b614d9bf265da0fa759e84b

浅比较 https://www.imweb.io/topic/598973c2c72aa8db35d2e291

展示组件和容器组件 https://juejin.im/post/5a52fe32f265da3e317e008b https://juejin.im/post/5af3d66ef265da0b7b35ec1d

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 一份走心的iOS开发规范前言约定(一)命名规范(二)编码规范2.14 内存管理规范本文参考文章其他有价值的文章

    VV木公子
  • iOS开发之UICollectionViewController系列(四) :一款功能强大的自定义瀑布流

    VV木公子
  • GCD信号量-dispatch_semaphore_t

    VV木公子
  • React Native基础&入门教程:以一个To Do List小例子,看props和state

    在上篇中,我们介绍了什么是Flexbox布局,以及如何使用Flexbox布局。还没有看过的小伙伴欢迎回到文章列表点击查看之前的文章了解。

    葡萄城控件
  • 【react】关于react框架使用的一些细节要点的思考

    ( _(:3 」∠)_给云友们提个建议,无论是API文档还是书籍,一定要多看几遍!特别是隔一段时间后,会有意想不到的收获的) 这篇文章主要是写关于学习react...

    外婆的彭湖湾
  • React 深入系列3:Props 和 State

    文:徐超,《React进阶之路》作者 授权发布,转载请注明作者及出处 ---- React 深入系列3:Props 和 State React 深...

    iKcamp
  • React中的-- 数据流

    简介 React的组件简单理解起来其实就是一个函数,这个函数会接收props和state作为参数,然后进行相应的逻辑处理,最终返回该组件的虚拟DOM展现。在Re...

    前朝楚水
  • (React 框架)React技术

      当时他们的团队在市面上没找到合适的MVC 框架,就自己写一个 JS 框架,用来架设 instagram(图片分享社交网路),2013年开源

    py3study
  • React之组件

    首先我们通过函数来创建一个组件,函数的名字即是组件的名字!另外有两个地方需要特别注意:

    用户1272076
  • React学习(六)-React中组件的数据-state

    一个组件最终渲染的数据结果,除了prop还有state,state代表的是当前组件的内部状态,你可以把组件看成一个'状态机",它是能够随着时间变化的数据,更多的...

    itclanCoder

扫码关注云+社区

领取腾讯云代金券