Ich verstehe, dass React-Tutorials und -Dokumentationen ohne Zweifel warnen, dass der Status nicht direkt mutiert werden sollte und dass alles durchlaufen sollte setState
.
Ich möchte verstehen, warum genau ich nicht einfach den Status direkt ändern und dann (in derselben Funktion) aufrufen kann, this.setState({})
nur um den auszulösen render
.
Beispiel: Der folgende Code scheint gut zu funktionieren:
const React = require('react');
const App = React.createClass({
getInitialState: function() {
return {
some: {
rather: {
deeply: {
embedded: {
stuff: 1
}}}}};
},
updateCounter: function () {
this.state.some.rather.deeply.embedded.stuff++;
this.setState({}); // just to trigger the render ...
},
render: function() {
return (
<div>
Counter value: {this.state.some.rather.deeply.embedded.stuff}
<br></br>
<button onClick={this.updateCounter}>inc</button>
</div>
);
}
});
export default App;
Ich bin alle dafür, Konventionen zu befolgen, aber ich möchte mein weiteres Verständnis dafür verbessern, wie ReactJS tatsächlich funktioniert und was schief gehen kann oder ob es mit dem obigen Code nicht optimal ist.
Die Hinweise in der this.setState
Dokumentation identifizieren grundsätzlich zwei Fallstricke:
- Wenn Sie den Status direkt mutieren und anschließend aufrufen,
this.setState
kann dies die von Ihnen vorgenommene Mutation ersetzen (überschreiben?). Ich sehe nicht, wie dies im obigen Code passieren kann. - Dies
setState
kannthis.state
auf asynchrone / verzögerte Weise effektiv mutieren. Wenn Sie alsothis.state
direkt nach dem Anruf darauf zugreifen , könnenthis.setState
Sie nicht garantiert auf den endgültigen mutierten Status zugreifen. Ich verstehe, dass dies kein Problem ist, wennthis.setState
der letzte Aufruf der Update-Funktion ist.
javascript
reactjs
Marcus Junius Brutus
quelle
quelle
setState
Dokumentation . Es behandelt einige der guten Gründe.Antworten:
Die React-Dokumente für
setState
haben Folgendes zu sagen:Wenn Sie
this.state
direkt ändern , erstellen Sie grundsätzlich eine Situation, in der diese Änderungen möglicherweise überschrieben werden.Bezogen auf Ihre erweiterten Fragen 1) und 2)
setState()
ist nicht unmittelbar. Es stellt einen Statusübergang in die Warteschlange, der auf dem basiert, was seiner Meinung nach vor sich geht und möglicherweise nicht die direkten Änderungen an enthältthis.state
. Da es nicht sofort angewendet, sondern in die Warteschlange gestellt wird, ist es durchaus möglich, dass dazwischen etwas geändert wird, sodass Ihre direkten Änderungen überschrieben werden.Wenn nichts anderes, sind Sie vielleicht besser dran, wenn Sie nur bedenken, dass nicht direktes Ändern
this.state
als gute Praxis angesehen werden kann. Möglicherweise wissen Sie persönlich, dass Ihr Code mit React so interagiert, dass diese Überschreibungen oder andere Probleme nicht auftreten können, aber Sie schaffen eine Situation, in der andere Entwickler oder zukünftige Updates plötzlich mit seltsamen oder subtilen Problemen konfrontiert werden.quelle
setState()
Systeme und des Rückrufsystems ein und gibt Ihnen eine gute Vorstellung davon, warum es ein Problem geben würde.Diese Antwort soll genügend Informationen liefern, um den Status nicht direkt in React zu ändern / zu mutieren.
Die Reaktion folgt dem unidirektionalen Datenfluss . Das heißt, der Datenfluss innerhalb der Reaktion sollte und wird voraussichtlich auf einer Kreisbahn liegen.
Datenfluss der Reaktion ohne Fluss
Damit React so funktioniert, haben Entwickler das React der funktionalen Programmierung ähnlich gemacht . Die Faustregel der funktionalen Programmierung ist Unveränderlichkeit . Lassen Sie es mich klar und deutlich erklären.
Wie funktioniert der unidirektionale Fluss?
states
sind ein Datenspeicher, der die Daten einer Komponente enthält.view
Rendern einer Komponente basiert auf dem Status.view
etwas auf dem Bildschirm geändert werden muss, sollte dieser Wert vom bereitgestellt werdenstore
.setState()
Funktion, die einenobject
neuen Status aufnimmtstates
undobject.assign()
den vorherigen Status vergleicht und zusammenführt (ähnlich ) und den neuen Status zum Statusdatenspeicher hinzufügt.view
Benutzer verbraucht, und auf dem Bildschirm angezeigt.Dieser Zyklus wird während der gesamten Lebensdauer der Komponente fortgesetzt.
Wenn Sie die obigen Schritte sehen, zeigt dies deutlich, dass beim Ändern des Status viele Dinge dahinter stehen. Wenn Sie also den Status direkt mutieren und
setState()
mit einem leeren Objekt aufrufen . Dasprevious state
wird mit Ihrer Mutation verschmutzt. Aufgrund dessen wird der flache Vergleich und die Zusammenführung von zwei Zuständen gestört oder wird nicht stattfinden, da Sie jetzt nur einen Zustand haben. Dies stört alle Lebenszyklusmethoden der Reaktion.Infolgedessen verhält sich Ihre App abnormal oder stürzt sogar ab. In den meisten Fällen hat dies keine Auswirkungen auf Ihre App, da alle Apps, die wir zum Testen verwenden, ziemlich klein sind.
Ein weiterer Nachteil der Mutation von
Objects
undArrays
in JavaScript besteht darin, dass Sie beim Zuweisen eines Objekts oder eines Arrays nur eine Referenz auf dieses Objekt oder dieses Array erstellen. Wenn Sie sie mutieren, sind alle Verweise auf dieses Objekt oder dieses Array betroffen. React behandelt dies auf intelligente Weise im Hintergrund und gibt uns einfach eine API, damit es funktioniert.Die häufigsten Fehler bei der Behandlung von Zuständen in React
// original state this.state = { a: [1,2,3,4,5] } // changing the state in react // need to add '6' in the array // bad approach const b = this.state.a.push(6) this.setState({ a: b })
Im obigen Beispiel
this.state.a.push(6)
wird der Status direkt mutiert. Das Zuweisen zu einer anderen Variablen und das Aufrufen entsprichtsetState
dem unten gezeigten. Da wir den Status ohnehin mutiert haben, macht es keinen Sinn, ihn einer anderen Variablen zuzuweisen undsetState
mit dieser Variablen aufzurufen .// same as this.state.a.push(6) this.setState({})
Die meisten Leute machen das. Das ist so falsch . Dies bricht die Schönheit von React und macht Sie zu einem schlechten Programmierer.
Was ist der beste Weg, um mit Zuständen in React umzugehen? Lassen Sie mich erklären.
Wenn Sie "etwas" im vorhandenen Status ändern müssen, erhalten Sie zuerst eine Kopie dieses "etwas" aus dem aktuellen Status.
// original state this.state = { a: [1,2,3,4,5] } // changing the state in react // need to add '6' in the array // create a copy of this.state.a // you can use ES6's destructuring or loadash's _.clone() const currentStateCopy = [...this.state.a]
Durch Mutieren
currentStateCopy
wird der ursprüngliche Zustand nicht mutiert. Führen Sie Operationen auscurrentStateCopy
und setzen Sie sie mit auf den neuen StatussetState()
.currentStateCopy.push(6) this.state({ a: currentStateCopy })
Das ist wunderschön, oder?
Auf diese Weise werden alle Referenzen von
this.state.a
erst betroffen, wenn wir sie verwendensetState
. Dies gibt Ihnen die Kontrolle über Ihren Code und hilft Ihnen dabei, elegante Tests zu schreiben und sich auf die Leistung des Codes in der Produktion zu verlassen.Zur Beantwortung Ihrer Frage,
Ja, das kannst du . Sie müssen sich jedoch den folgenden Konsequenzen stellen.
state
über alle Komponenten.Unveränderlichkeit ist nicht erforderlich, da JavaScript Single-Threaded ist. Aber es ist eine gute Übung, die Ihnen auf lange Sicht helfen wird.
PS. Ich habe ungefähr 10000 Zeilen veränderbaren React JS-Codes geschrieben. Wenn es jetzt kaputt geht, weiß ich nicht, wo ich suchen soll, da alle Werte irgendwo mutiert sind. Als ich das merkte, fing ich an, unveränderlichen Code zu schreiben. Vertrau mir! Das ist das Beste, was Sie mit einem Produkt oder einer App tun können.
Hoffe das hilft!
quelle
slice
, ES6-Destrukturierung usw.) sind flach. Wenn Sie ein verschachteltes Array oder verschachtelte Objekte haben, müssen Sie sich andere Methoden zum tiefen Kopieren ansehen, z. B.JSON.parse(JSON.stringify(obj))
(obwohl diese spezielle Methode nicht funktioniert, wenn Ihr Objekt Zirkelverweise enthält)._.cloneDeep
ausreichennpm install lodash.clonedeep
und mit verwendenlet cloneDeep = require("lodash.clonedeep");
.die einfachste Antwort auf "
dreht sich alles um die Aktualisierungsphase.
Wenn wir den Status einer Komponente aktualisieren, werden auch alle untergeordneten Komponenten gerendert. oder unser gesamter Komponentenbaum gerendert.
Aber wenn ich sage, dass unser gesamter Komponentenbaum gerendert wird, bedeutet das nicht, dass das gesamte DOM aktualisiert wird. Wenn eine Komponente gerendert wird, erhalten wir im Grunde ein Reaktionselement, das unseren virtuellen Dom aktualisiert.
React wird dann das virtuelle DOM betrachten, es hat auch eine Kopie des alten virtuellen DOM, deshalb sollten wir den Status nicht direkt aktualisieren , damit wir zwei verschiedene Objektreferenzen im Speicher haben können, wir haben das alte virtuelle DOM als sowie das neue virtuelle DOM.
Wenn Sie dann reagieren, wird herausgefunden, was geändert wurde, und basierend darauf wird das reale DOM entsprechend aktualisiert.
ich hoffe es hilft.
quelle
Um zu vermeiden, dass Sie jedes Mal eine Kopie von erstellen
this.state.element
, können Sie ein Update mit$set or $push
oder vielen anderen von immutability-helper verwendenz.B:
import update from 'immutability-helper'; const newData = update(myData, { x: {y: {z: {$set: 7}}}, a: {b: {$push: [9]}} });
quelle
setState löst das Rendern der Komponenten aus. Wenn wir den Status immer wieder aktualisieren möchten, müssen wir setState festlegen, da dies sonst nicht ordnungsgemäß funktioniert.
quelle
Mein derzeitiges Verständnis basiert auf dieser und dieser Antwort:
WENN Sie nicht verwenden
shouldComponentUpdate
oder andere Lifecycle - Methoden (wiecomponentWillReceiveProps
,componentWillUpdate
undcomponentDidUpdate
) , wo Sie die alten und neuen Requisiten vergleichen / ZustandDANN
Es ist in Ordnung zu mutieren
state
und dann anzurufensetState()
, sonst ist es nicht in Ordnung.quelle