浅析比较如何在反应

时间:2016-03-18 12:33:12

标签: javascript reactjs

在React的this documentation中,据说

  

shallowCompare对当前的props和nextProps对象以及当前的state和nextState对象执行浅等式检查。

我无法理解的是,如果它浅显比较对象,那么shouldComponentUpdate方法将始终返回true,如

  

我们不应该改变状态。

如果我们没有改变状态,那么比较将始终返回false,因此shouldComponent更新将始终返回true。我对它是如何工作感到困惑,我们将如何覆盖它以提高性能。

9 个答案:

答案 0 :(得分:86)

浅比较检查是否相等。比较标量值(数字,字符串)时,它会比较它们的值。比较对象时,它不会比较它们的属性 - 只比较它们的引用(例如"它们是否指向同一个对象?)。

让我们考虑一下user对象的形状

user = {
  name: "John",
  surname: "Doe"
}

示例1:

const user = this.state.user;
user.name = "Jane";

console.log(user === this.state.user); // true

请注意您更改了用户名。即使有这种变化,对象也是相同的。它们的引用完全相同。

示例2:

const user = clone(this.state.user);
console.log(user === this.state.user); // false

现在,在没有对对象属性进行任何更改的情况下,它们完全不同。通过克隆原始对象,您可以创建具有不同引用的新副本。

克隆功能可能看起来像这样(ES6语法)

const clone = obj => Object.assign({}, ...obj);

浅层比较是检测变化的有效方法。它希望你不要改变数据。

答案 1 :(得分:12)

浅比较是指使用“ ===“或严格相等性完成比较对象的属性,并且不会对属性进行更深层的比较。例如

// a simple implementation of the shallowCompare.
// only compares the first level properties and hence shallow.
// state updates(theoretically) if this function returns true.
function shallowCompare(newObj, prevObj){
    for (key in newObj){
        if(newObj[key] !== prevObj[key]) return true;
    }
    return false;
}
// 
var game_item = {
    game: "football",
    first_world_cup: "1930",
    teams: {
         North_America: 1,
         South_America: 4,
         Europe: 8 
    }
}
// Case 1:
// if this be the object passed to setState
var updated_game_item1 = {
    game: "football",
    first_world_cup: "1930",
    teams: {
         North_America: 1,
         South_America: 4,
         Europe: 8 
    }
}
shallowCompare(updated_game_item1, game_item); // true - meaning the state
                                               // will update.

尽管两个对象看起来相同,但是game_item.teamsupdated_game_item.teams的引用不同。对于2个相同的对象,它们应指向相同的对象。 因此,这导致状态被评估为要更新

// Case 2:
// if this be the object passed to setState
var updated_game_item2 = {
    game: "football",
    first_world_cup: "1930",
    teams: game_item.teams
}
shallowCompare(updated_game_item2, game_item); // false - meaning the state
                                               // will not update.

这一次,为了严格比较,每个属性都返回true,因为新对象和旧对象中的teams属性都指向同一个对象。

// Case 3:
// if this be the object passed to setState
var updated_game_item3 = {
    first_world_cup: 1930
}
shallowCompare(updated_game_item3, game_item); // true - will update

updated_game_item3.first_world_cup属性未通过严格评估,因为1930是数字,而game_item.first_world_cup是字符串。如果比较宽松(==),这将过去。尽管如此,这也会导致状态更新。

附加说明:

  1. 进行深度比较是没有意义的,因为如果状态对象被深度嵌套,则会对性能产生重大影响。但是,如果它不是太嵌套,并且您仍然需要深入比较,请在shouldComponentUpdate中实现它,并检查是否足够。
  2. 您当然可以直接直接更改状态对象,但不会影响组件的状态,因为在setState方法流中,它的响应会实现组件更新周期的挂钩。如果直接更新状态对象以故意避免组件生命周期挂钩,那么可能应该使用简单的变量或对象而非状态对象来存储数据。

答案 2 :(得分:8)

在React中还有legacy explanation浅层比较:

  

shallowCompare对当前的props和nextProps对象以及当前的state和nextState对象执行浅等式检查。

     

它通过迭代被比较对象的键来实现这一点,并且当每个对象中的键值不完全相等时返回true。

UPD Current documentation说浅层比较:

  

如果React组件的render()函数在给定相同的props和state的情况下呈现相同的结果,则在某些情况下可以使用React.PureComponent来提升性能。

     

React.PureComponent的shouldComponentUpdate()只是浅析对象。如果这些包含复杂的数据结构,则可能会产生更深层次差异的假阴性。只有当你希望有简单的道具和状态时才扩展PureComponent,或者当你知道深层数据结构已经改变时使用forceUpdate()

UPD2:我认为Reconciliation也是浅析比较理解的重要主题。

答案 3 :(得分:6)

  

浅比较通过检查两个基本类型的值是否相等(例如字符串,数字和对于对象的类型)来进行比较,即只检查引用。因此,如果您浅比较一个深层嵌套的对象,它将只检查引用而不是该对象内部的值。

答案 4 :(得分:1)

被接受的答案可能对某些人有些误导。

user = {
  name: "John",
  surname: "Doe"
}

const user = this.state.user;
user.name = "Jane";

console.log(user === this.state.user); // true

该声明特别是“请注意,您更改了用户名。即使更改对象相同,它们的引用也完全相同。”

对javascript中的对象执行以下操作时:

const a = {name: "John"};
const b = a;

更改两个变量中的任何一个都将更改它们,因为它们具有相同的引用。这就是为什么它们总是彼此相等(==, ===, Object.is())的原因。

现在是React,以下是浅层比较函数: https://github.com/facebook/fbjs/blob/master/packages/fbjs/src/core/shallowEqual.js

/**
 * Performs equality by iterating through keys on an object and returning false
 * when any key has values which are not strictly equal between the arguments.
 * Returns true when the values of all keys are strictly equal.
 */
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

对于非原始对象(对象),它会检查:

  1. 如果第一个对象(使用Object.is())等于第二个对象。
  2. 如果不是,则检查第一个对象中的每个键值对(使用Object.is())是否等于第二个。这是针对第一级密钥完成的。如果该对象具有其值为另一个对象的键,则此函数不会在该对象的更深处检查是否相等。

答案 5 :(得分:0)

如果prevObj具有newObj没有的密钥,则上述@supi(https://stackoverflow.com/a/51343585/800608)的浅相等片段将失败。这是一个应该考虑到这一点的实现:

const shallowEqual = (objA, objB) => {
  if (!objA || !objB) {
    return objA === objB
  }
  return !Boolean(
    Object
      .keys(Object.assign({}, objA, objB))
      .find((key) => objA[key] !== objB[key])
  )
}

请注意,如果没有Polyfill,以上内容在资源管理器中将不起作用。

答案 6 :(得分:0)

有一个带有示例的实现。

pip install python-docx

答案 7 :(得分:0)

很容易理解。首先需要了解纯组件和常规组件,如果一个组件有即将到来的 props 或 state 发生变化,那么它会再次重新渲染该组件。 如果不是那么不是。 在常规组件 shouldComponentUpdate 中默认为 true。而在纯组件中,只有状态随 diff 值变化的时间。

那么现在什么是浅层组件或浅层? 让我们举一个简单的例子。 让 a = [1,2,3], 让 b = [1,2,3],

a == b ==> 浅取假, a == c ==> 浅把它当成真的。 c 有任何差异值。

现在我想你可以理解了。普通组件和纯组件与浅组件的差异 如果你喜欢它,也喜欢分享和订阅我的 YouTube 频道 https://www.youtube.com/muosigmaclasses

谢谢。

答案 8 :(得分:0)

我觉得没有一个答案实际上解决了您问题中的关键部分,答案只是解释了什么是浅比较(无论它们是指 === 或 {{ 1}} 运算符或 React 的 == 函数)

为了回答你的问题,到目前为止我对 React 的理解让我相信确实是的通过不直接改变状态然后 shallowCompare() 将始终返回 true 从而总是导致重新渲染无论我们在 shouldComponentUpdate 中传递什么对象,即使传递给 setState 的对象持有存储在当前状态中的相同值

示例:

假设我有一个带有当前状态和功能的 React.Component:

setState

您可以看到 this.state = {data: {num: 1}} // current state object foo() { // something will cause this function to called, thus calling setState this.setState( {data: {num: 1}} ); // new state object } 传递了相同的对象(按值),但是简单的 React 不够聪明,无法意识到该组件不应更新/重新渲染。

要克服这个问题,您必须实施您自己的 setState 版本,在该版本中您自己对您认为应该考虑的 state/props 元素进行深入比较。

查看简要回答此问题的 this article on lucybain.com