Javascript 浅比较如何在反应中工作
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/36084515/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How does shallow compare work in react
提问by Ajay Gaur
In this documentationof React, it is said that
shallowCompare performs a shallow equality check on the current props and nextProps objects as well as the current state and nextState objects.
ShallowCompare 对当前 props 和 nextProps 对象以及当前 state 和 nextState 对象执行浅层相等性检查。
The thing which I am unable to understand is If It shallowly compares the objects then shouldComponentUpdate method will always return true, as
我无法理解的是,如果它对对象进行浅层比较,则 shouldComponentUpdate 方法将始终返回 true,如
We should not mutate the states.
我们不应该改变状态。
and if we are not mutating the states then the comparison will always return false and so the shouldComponent update will always return true. I am confused about how it is working and how will we override this to boost the performance.
如果我们不改变状态,那么比较将始终返回 false,因此 shouldComponent 更新将始终返回 true。我对它是如何工作的以及我们将如何覆盖它以提高性能感到困惑。
回答by Andreyco
Shallow compare does check for equality. When comparing scalar values (numbers, strings) it compares their values. When comparing objects, it does not compare their's attributes - only their references are compared (e.g. "do they point to same object?).
浅比较确实检查相等性。在比较标量值(数字、字符串)时,它会比较它们的值。当比较对象时,它不比较它们的属性——只比较它们的引用(例如“它们是否指向同一个对象?)。
Let's consider following shape of userobject
让我们考虑以下user物体的形状
user = {
name: "John",
surname: "Doe"
}
Example 1:
示例 1:
const user = this.state.user;
user.name = "Jane";
console.log(user === this.state.user); // true
Notice you changed users name. Even with this change objects are equal. They references are exactly same.
请注意,您更改了用户名。即使有这种变化,对象也是平等的。它们的引用完全相同。
Example 2:
示例 2:
const user = clone(this.state.user);
console.log(user === this.state.user); // false
Now, without any changes to object properties they are completely different. By cloning original object you create new copy, with different reference.
现在,无需对对象属性进行任何更改,它们就完全不同了。通过克隆原始对象,您可以创建具有不同引用的新副本。
Clone function might look as this (ES6 syntax)
克隆函数可能看起来像这样(ES6 语法)
const clone = obj => Object.assign({}, ...obj);
Shallow compare is efficient way to detect changes. It expect you don't mutate data.
浅比较是检测变化的有效方法。它希望您不会改变数据。
回答by supi
shallow comparison is when the properties of the objects being compared is done using "===" or strict equality and will not conduct comparisons deeper into the properties. for e.g.
浅比较是当被比较的对象的属性使用“===”或严格相等时完成,并且不会对属性进行更深入的比较。例如
// 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.
Although both the objects appear to be same, game_item.teamsis not the same reference as updated_game_item.teams. For 2 objects to be same, they should point to the same object.
Thus this results in the state being evaluated to be updated
尽管这两个对象看起来相同,game_item.teams但与updated_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.
This time every one of the properties return true for the strict comparison as the teams property in the new and old object point to the same object.
这次每个属性都返回真以进行严格比较,因为新旧对象中的团队属性指向同一个对象。
// 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
The updated_game_item3.first_world_cupproperty fails the strict evaluation as 1930 is a number while game_item.first_world_cupis a string. Had the comparison been loose (==) this would have passed. Nonetheless this will also result in state update.
该updated_game_item3.first_world_cup属性未通过严格评估,因为 1930 是一个数字game_item.first_world_cup而是一个字符串。如果比较松散(==),这将通过。尽管如此,这也将导致状态更新。
Additional Notes:
补充说明:
- Doing deep compare is pointless as it would significantly effect performance if the state object is deeply nested. But if its not too nested and you still need a deep compare, implement it in shouldComponentUpdate and check if that suffices.
- You can definitely mutate the state object directly but the state of the components would not be affected, since its in the setState method flow that react implements the component update cycle hooks. If you update the state object directly to deliberately avoid the component life-cycle hooks, then probably you should be using a simple variable or object to store the data and not the state object.
- 进行深度比较是没有意义的,因为如果状态对象是深度嵌套的,它会显着影响性能。但是如果它不是太嵌套并且您仍然需要深入比较,请在 shouldComponentUpdate 中实现它并检查是否足够。
- 你绝对可以直接改变 state 对象,但组件的 state 不会受到影响,因为它在 setState 方法流中,react 实现了组件更新循环钩子。如果您直接更新状态对象以故意避免组件生命周期挂钩,那么您可能应该使用简单的变量或对象来存储数据而不是状态对象。
回答by akhil choudhary
Shallow compare works by checking if two values are equal in case of primitive typeslike string, numbers and in case of object it just checks the reference. So if you shallow compare a deep nested object it will just check the reference not the values inside that object.
浅比较的工作原理是在字符串、数字等原始类型的情况下检查两个值是否相等,在对象的情况下它只检查引用。因此,如果您对深度嵌套的对象进行浅比较,它只会检查引用而不是该对象内的值。
回答by valex
There is also legacy explanationof shallow compare in React:
React 中也有对浅比较的遗留解释:
shallowCompare performs a shallow equality check on the current props and nextProps objects as well as the current state and nextState objects.
It does this by iterating on the keys of the objects being compared and returning true when the values of a key in each object are not strictly equal.
ShallowCompare 对当前 props 和 nextProps 对象以及当前 state 和 nextState 对象执行浅层相等性检查。
它通过迭代要比较的对象的键并在每个对象中的键的值不严格相等时返回 true 来实现这一点。
UPD: Current documentationsays about shallow compare:
UPD:当前文档说明了浅比较:
If your React component's render() function renders the same result given the same props and state, you can use React.PureComponent for a performance boost in some cases.
React.PureComponent's shouldComponentUpdate() only shallowly compares the objects. If these contain complex data structures, it may produce false-negatives for deeper differences. Only extend PureComponent when you expect to have simple props and state, or use forceUpdate() when you know deep data structures have changed
如果你的 React 组件的 render() 函数在给定相同的 props 和 state 的情况下呈现相同的结果,你可以在某些情况下使用 React.PureComponent 来提升性能。
React.PureComponent 的 shouldComponentUpdate() 只是对对象进行浅层比较。如果这些包含复杂的数据结构,则可能会对更深层次的差异产生假阴性。仅当您希望拥有简单的 props 和 state 时才扩展 PureComponent,或者在您知道深层数据结构已更改时使用 forceUpdate()
UPD2:I think Reconciliationis also important theme for shallow compare understanding.
UPD2:我认为和解也是浅比较理解的重要主题。
回答by mzedeler
The shallow equal snippet by @supi above (https://stackoverflow.com/a/51343585/800608) fails if prevObjhas a key that newObjdoesn't have. Here is an implementation that should take that into account:
如果有一个没有的键,上面@supi ( https://stackoverflow.com/a/51343585/800608)的浅等片段将失败。这是一个应该考虑到这一点的实现:prevObjnewObj
const shallowEqual = (objA, objB) => {
if (!objA || !objB) {
return objA === objB
}
return !Boolean(
Object
.keys(Object.assign({}, objA, objB))
.find((key) => objA[key] !== objB[key])
)
}
Note that the above doesn't work in Explorer without polyfills.
请注意,上述内容在没有 polyfills 的资源管理器中不起作用。
回答by Max Starling
There is an implementation with examples.
有一个带有示例的实现。
const isObject = value => typeof value === 'object' && value !== null;
const compareObjects = (A, B) => {
const keysA = Object.keys(A);
const keysB = Object.keys(B);
if (keysA.length !== keysB.length) {
return false;
}
return !keysA.some(key => !B.hasOwnProperty(key) || A[key] !== B[key]);
};
const shallowEqual = (A, B) => {
if (A === B) {
return true;
}
if ([A, B].every(Number.isNaN)) {
return true;
}
if (![A, B].every(isObject)) {
return false;
}
return compareObjects(A, B);
};
const a = { field: 1 };
const b = { field: 2 };
const c = { field: { field: 1 } };
const d = { field: { field: 1 } };
console.log(shallowEqual(1, 1)); // true
console.log(shallowEqual(1, 2)); // false
console.log(shallowEqual(null, null)); // true
console.log(shallowEqual(NaN, NaN)); // true
console.log(shallowEqual([], [])); // true
console.log(shallowEqual([1], [2])); // false
console.log(shallowEqual({}, {})); // true
console.log(shallowEqual({}, a)); // false
console.log(shallowEqual(a, b)); // false
console.log(shallowEqual(a, c)); // false
console.log(shallowEqual(c, d)); // false

