Wie reagiert der flache Vergleich?

91

In dieser Dokumentation von React heißt es:

flatCompare führt eine flache Gleichheitsprüfung für die aktuellen Requisiten- und nextProps-Objekte sowie für die aktuellen Status- und nextState-Objekte durch.

Das, was ich nicht verstehen kann, ist: Wenn es die Objekte flach vergleicht, gibt die Methode shouldComponentUpdate immer true zurück, as

Wir sollten die Staaten nicht mutieren.

und wenn wir die Zustände nicht mutieren, gibt der Vergleich immer false zurück, und daher sollte das shouldComponent-Update immer true zurückgeben. Ich bin verwirrt darüber, wie es funktioniert und wie wir dies überschreiben werden, um die Leistung zu steigern.

Ajay Gaur
quelle

Antworten:

128

Flacher Vergleich prüft auf Gleichheit. Beim Vergleich von Skalarwerten (Zahlen, Zeichenfolgen) werden deren Werte verglichen. Beim Vergleichen von Objekten werden deren Attribute nicht verglichen - nur ihre Referenzen werden verglichen (z. B. "Zeigen sie auf dasselbe Objekt?").

Betrachten wir die folgende Form des userObjekts

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

Beispiel 1:

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

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

Beachten Sie, dass Sie den Benutzernamen geändert haben. Auch bei dieser Änderung sind Objekte gleich. Die Referenzen sind genau gleich.

Beispiel 2:

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

Jetzt sind sie ohne Änderungen an den Objekteigenschaften völlig anders. Durch Klonen des Originalobjekts erstellen Sie eine neue Kopie mit einer anderen Referenz.

Die Klonfunktion könnte folgendermaßen aussehen (ES6-Syntax)

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

Ein flacher Vergleich ist eine effiziente Methode, um Änderungen zu erkennen. Es wird erwartet, dass Sie keine Daten mutieren.

Andreyco
quelle
Wenn wir also Code schreiben und skalare Werte haben, sollten wir sie dann mutieren, denn wenn wir sie klonen, gibt die Gleichheitsprüfung false zurück?
Ajay Gaur
27
@AjayGaur Diese Antwort kann Ihnen zwar helfen, die strikte Gleichheit (===) in JavaScript zu verstehen, sagt jedoch nichts über die Funktion flatCompare () in React aus (ich denke, der Antwortende hat Ihre Frage falsch verstanden). Was flatCompare () tatsächlich tut, befindet sich in dem von Ihnen bereitgestellten Dokument: Iterieren der Schlüssel der zu vergleichenden Objekte und Zurückgeben von true, wenn die Werte eines Schlüssels in jedem Objekt nicht genau gleich sind. Wenn Sie diese Funktion immer noch nicht verstehen und warum Sie den Status nicht ändern sollten, kann ich eine Antwort für Sie schreiben.
Sunquan
6
Es ist nicht wahr. Sieh dir das an. github.com/facebook/fbjs/blob/master/packages/fbjs/src/core/…
Bright Lee
5
Diese Antwort beschreibt den Unterschied zwischen den Gleichheitsoperatoren (==) und den strengen Gleichheitsoperatoren (===) in JS. Die Frage betrifft einen flachen Vergleich, der in React implementiert wird, indem die Gleichheit zwischen allen Requisiten zweier Objekte überprüft wird.
Mateo Hrastnik
@sunquan kannst du bitte eine antwort darauf schreiben?
Ajay Gaur
29

Ein flacher Vergleich liegt vor, wenn die Eigenschaften der zu vergleichenden Objekte mit "===" oder strikter Gleichheit durchgeführt werden und keine tieferen Vergleiche der Eigenschaften durchgeführt werden. für zB

// 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.

Obwohl beide Objekte gleich zu sein scheinen, game_item.teamsist dies nicht die gleiche Referenz wie updated_game_item.teams. Damit 2 Objekte gleich sind, sollten sie auf dasselbe Objekt zeigen. Dies führt also dazu, dass der zu bewertende Zustand aktualisiert wird

// 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.

Diesmal gibt jede der Eigenschaften für den strengen Vergleich true zurück, da die Team-Eigenschaft im neuen und alten Objekt auf dasselbe Objekt verweist.

// 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

Die updated_game_item3.first_world_cupEigenschaft besteht die strenge Bewertung nicht, da 1930 eine Zahl ist, während game_item.first_world_cupes sich um eine Zeichenfolge handelt. Wäre der Vergleich locker gewesen (==), wäre dies vorbei. Dies führt jedoch auch zu einer Statusaktualisierung.

Zusätzliche Bemerkungen:

  1. Ein tiefer Vergleich ist sinnlos, da dies die Leistung erheblich beeinträchtigen würde, wenn das Statusobjekt tief verschachtelt ist. Wenn es jedoch nicht zu verschachtelt ist und Sie dennoch einen umfassenden Vergleich benötigen, implementieren Sie es in shouldComponentUpdate und prüfen Sie, ob dies ausreicht.
  2. Sie können das Statusobjekt definitiv direkt mutieren, aber der Status der Komponenten wird nicht beeinflusst, da er im setState-Methodenfluss, der reagiert, die Hooks für den Komponentenaktualisierungszyklus implementiert. Wenn Sie das Statusobjekt direkt aktualisieren, um die Hooks des Komponentenlebenszyklus absichtlich zu vermeiden, sollten Sie wahrscheinlich eine einfache Variable oder ein einfaches Objekt zum Speichern der Daten und nicht des Statusobjekts verwenden.
supi
quelle
Bedeutet dies nicht, dass die Komponente niemals neu gerendert wird, wenn ich ein Objekt über Requisiten übergebe oder den Status mit dem nächsten Status vergleiche, denn selbst wenn sich die Eigenschaften dieses Objekts geändert haben, verweist sie immer noch auf dasselbe Objekt, was zu folgendem Ergebnis führt falsch, also nicht neu rendern?
Javascripting
@javascripting - Aus diesem Grund würden Sie Ihre Objekte klonen (z. B. mit Object.assign ()), wenn sie sich ändern, anstatt sie zu mutieren, damit React weiß, wann sich die Referenz ändert und die Komponente aktualisiert werden muss.
Mac_W
Wenn prevObjein Schlüssel enthalten ist, newObjder nicht vorhanden ist, schlägt der Vergleich fehl.
mzedeler
@mzedeler - wird es nicht, weil das "for in" auf newObj und nicht auf prevObj iteriert. Versuchen Sie, den Code wie in der Browser-Entwicklerkonsole auszuführen. Bitte nehmen Sie diese Implementierung des flachen Vergleichs nicht zu ernst, dies dient nur zur Demonstration des Konzepts
supi
Was ist mit Arrays?
Juan De la Cruz
22

Ein flacher Vergleich funktioniert, indem überprüft wird, ob zwei Werte bei primitiven Typen wie Zeichenfolge, Zahlen gleich sind, und bei Objekten nur die Referenz überprüft wird . Wenn Sie also ein tief verschachteltes Objekt flach vergleichen, wird nur die Referenz und nicht die Werte in diesem Objekt überprüft.

Akhil Choudhary
quelle
10

Es gibt auch eine Legacy-Erklärung für flache Vergleiche in React:

flatCompare führt eine flache Gleichheitsprüfung für die aktuellen Requisiten- und nextProps-Objekte sowie für die aktuellen Status- und nextState-Objekte durch.

Dazu werden die Schlüssel der zu vergleichenden Objekte iteriert und true zurückgegeben, wenn die Werte eines Schlüssels in jedem Objekt nicht genau gleich sind.

UPD : Aktuelle Dokumentation sagt über flache Vergleiche:

Wenn die render () - Funktion Ihrer React-Komponente bei gleichen Requisiten und Status dasselbe Ergebnis liefert, können Sie React.PureComponent in einigen Fällen für eine Leistungssteigerung verwenden.

React.PureComponent's shouldComponentUpdate () vergleicht die Objekte nur flach. Wenn diese komplexe Datenstrukturen enthalten, kann dies zu falsch negativen Ergebnissen für tiefere Unterschiede führen. Erweitern Sie PureComponent nur, wenn Sie einfache Requisiten und Status erwarten, oder verwenden Sie forceUpdate (), wenn Sie wissen, dass sich tiefe Datenstrukturen geändert haben

UPD2: Ich denke, Versöhnung ist auch ein wichtiges Thema für ein flaches Vergleichsverständnis.

Valex
quelle
1

Das flache gleiche Snippet von @supi oben ( https://stackoverflow.com/a/51343585/800608 ) schlägt fehl, wenn prevObjein Schlüssel vorhanden ist, newObjder nicht vorhanden ist. Hier ist eine Implementierung, die dies berücksichtigen sollte:

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

Beachten Sie, dass das oben Genannte im Explorer ohne Polyfills nicht funktioniert.

mzedeler
quelle
Sieht gut aus, aber in diesem Fall werden zwei NaN-Rückgaben falsch übergeben, während dies in der vorherigen Antwort der Fall ist.
Spadar Shut
0

Es gibt eine Implementierung mit Beispielen.

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

Max Starling
quelle