React中的浅比较是如何工作的?

本文翻译自https://www.chakshunyu.com/blog/how-does-shallow-comparison-work-in-react/

浅比较这个概念在React开发过程中很常见。它在不同的过程中扮演着关键的角色,也可以在React组件生命周期的几个地方找到。判断class组件是否应该更新、React hood的依赖数组、通React.memo 缓存处理等例子

如果曾经阅读过官方的React文档,我们可能会经常到看到浅比较这个概念。但通常只是一个比较简单的解释。所以,本文将研究浅比较的概念,它到底是什么、如何工作,并会得到一些我们可能不知道的结论

深入浅比较的实现

最直接了解浅比较的方式就是去深入它的实现。相应的代码可以在React Github项目shared包中的shallowEqual.js找到。代码如下

import is from './objectIs';
import hasOwnProperty from './hasOwnProperty';


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++) {
    const currentKey = keysA[i];
    if (
      !hasOwnProperty.call(objB, currentKey) ||
      !is(objA[currentKey], objB[currentKey])
    ) {
      return false;
    }
  }

  return true;
}

这个函数做了不少事情,我们一步一步看这个函数

function shallowEqual(objA: mixed, objB: mixed): boolean {
    // ...
}

函数接收两个入参作为被比较的对象。这个代码使用了Flow作为类型检测系统而不是使用TypeScript。两个函数的参数都使用了Flow中的mixed类型(类似TypeScript中的unknnown)。这表明它们可以是任意类型。

import is from './objectIs';

function shallowEqual(objA: mixed, objB: mixed): boolean {
  if (is(objA, objB)) {
    return true;
  }
    // ...
}

首先使用React的内部实现的is方法对两个函数参数进行比较。这个引入的is内部方法和js中的Object.js几乎没有区别。这个比较函数和常用的===基本相同,除了两个例外

  • Object.is+0-0当作不相等,而===把他们当作相等
  • Object.isNumber.NaNNumber.NaN当作相等,而===把他们当作不相等

基本上第一个条件分支能处理如下简单的情况:如果两个参数有相同的值,如原始值相等、或对象的引用相等,它们会被认为相等

function shallowEqual(objA: mixed, objB: mixed): boolean {
    // ...

  if (
    typeof objA !== 'object' ||
    objA === null ||
    typeof objB !== 'object' ||
    objB === null
  ) {
    return false;
  }

    // ...
}

处理了简单情况下的值相等或者对象引用相等后我们需要去比较更复杂的结构。如果其中一个参数是原始值,前面的比较仍然会漏掉这种情况

为了确保我们下面是比较两个复杂的数据结构,我们还需要检查是否其中一个参数不是对象或者是null。前一个检查确保我们处理的两个参数是对象或数组,而后一个检查是过滤掉null,因为的typeof null === 'object'。如果两个条件都成立那么处理的两个参数肯定是不相等的(否则前面的判断就会将它们过滤),所以浅比较返回false。

function shallowEqual(objA: mixed, objB: mixed): boolean {
    // ...

  const keysA = Object.keys(objA);
  const keysB = Object.keys(objB);

  if (keysA.length !== keysB.length) {
    return false;
  }

    // ...
}

现在可以确定我们只处理数组和对象。因此可以把重点放在复杂数据结构的比较上

首先,我们可以简单比较它们的键的数量是否相等。如果不是,他们就不会浅比较相等,这可以提高检查的效率。我们使用Object.keys获取它们的键的数量。对于对象,键数组由实际的键组成;而对于数组,键数组将由数组的索引组成。

import hasOwnProperty from './hasOwnProperty';

function shallowEqual(objA: mixed, objB: mixed): boolean {
    // ...

  // Test for A's keys different from B.
  for (let i = 0; i < keysA.length; i++) {
    const currentKey = keysA[i];
    if (
      !hasOwnProperty.call(objB, currentKey) ||
      !is(objA[currentKey], objB[currentKey])
    ) {
      return false;
    }
  }

  return true;
}

最后,我们遍历两个函数参数的值并逐个比较它们是否相等。使用上一步中生成的键数组,并使用hasOwnProperty检查键是否实际上是对象自身的属性,使用Object.is函数进行值比较

如果存在对象上的某个值不相等,那么通过浅比较就可以认为它们不相等。因此可以提前结束循环,并直接shallow wEqual函数返回false。如果所有的值都是相等那么我们可以通过浅比较函数判断两个参数相等,函数返回true

有趣的东西

我们已经了解了简单的比较和它背后的实现,也可以从中知道到一些有趣的东西:

  • 浅比较并不是使用全等===,而是使用Object.is

  • 浅比较中,空对象和空数组会被认为相等

  • 浅比较中,一个以索引值作为键的对象和一个在相应各下标处具有相同值的数组相等。如{0:2,1:3}等于[2,3]

  • 由于使用Object.is而不是使用===+0-0在浅比较中是不相等的。并且NaNNaN也认为不相等。这也适用于复杂结构内部的比较

  • 虽然两个直接创建的对象(或数组)通过浅比较是相等的({}[]),但嵌套的数组、对象是不相等的。如{someKey:{}{someKey:[]}浅比较是不相等的)

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在React,state指的是组件内部维护的一个状态,它可以存储组件的数据,控制组件的行为和渲染。state是一个对象,可以通过this.state来访问它,通过this.setState来更新它。当组件的state发生改变时,React会自动重新渲染组件,以反映出最新的状态。通过使用state,我们可以构建出可交互的、动态的React组件。 ### 回答2: 在React,state是组件用于存储和管理数据的一个对象。它代表组件的内部状态,并且可以随着时间的推移而改变。state的值可以通过setState方法进行更新。 使用state可以使组件根据用户的操作或其他事件改变其行为和外观。当state的值发生改变时,React会根据新的state值重新渲染组件,并更新UI以反映最新的数据。 在组件的构造函数,可以通过this.state来初始化state的值。在其他地方,可以通过this.state访问和修改state。 React的state是被视为私有的,只能在拥有它的组件内部使用。它不会被子组件继承,子组件需要通过props从父组件获取数据。 state的变化通常是由用户的交互或组件自身的逻辑触发的,例如点击按钮、输入文本等。当state的变化引发组件重新渲染时,React会智能地更新组件的差异部分,以提高性能。 需要注意的是,直接修改state的值是不被允许的,必须使用setState方法。这是因为React会将state的更新视为异步操作,并批量处理多个state的更新,以优化性能。如果直接修改state的值,很可能会导致组件的不可预测行为。 总而言之,state在React扮演着管理和更新组件数据的重要角色,它使组件能够根据不同的状态展示不同的UI,并且能够响应用户的交互。 ### 回答3: 在React,state(状态)是一种存储和管理组件数据的机制。每个React组件都可以有自己的state对象。State是一个JavaScript对象,包含组件的数据和状态变量。 React组件的state可以被修改和更新,并且当state的值发生改变后,React会自动重新渲染组件。这使得在React应用程序可以轻松地跟踪和改变数据,并相应地更新用户界面。 使用state,可以将组件内部的数据封装在组件自身内部,而不必依赖外部变量或其他组件传递的数据。组件可以通过setState方法来修改自己的state,该方法会自动触发组件的重新渲染。 state的值与props的值有所不同。props是从父组件传递给子组件的值,子组件不能直接修改props的值。而state是每个组件私有的,并且仅在该组件使用和修改。 state的使用可以使代码更具可读性和可维护性,同时也能提高应用程序的性能。通过合理地使用state,可以实现组件之间的通信和交互,以及对用户操作的响应。 总之,state是React的一种数据机制,用于存储和管理组件的数据和状态变量,并且能够实现动态更新用户界面。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值