Ich weiß, dass React Statusaktualisierungen zur Leistungsoptimierung asynchron und stapelweise durchführen kann. Daher können Sie niemals darauf vertrauen, dass der Status nach dem Aufruf aktualisiert wird setState
. Aber vertrauen können Sie reagieren auf den Zustand in der gleichen Reihenfolge zu aktualisieren , wie setState
aufgerufen wird für
- die gleiche Komponente?
- verschiedene Komponenten?
Klicken Sie in den folgenden Beispielen auf die Schaltfläche:
1. Gibt es jemals die Möglichkeit, dass a falsch und b wahr ist für:
class Container extends React.Component {
constructor(props) {
super(props);
this.state = { a: false, b: false };
}
render() {
return <Button onClick={this.handleClick}/>
}
handleClick = () => {
this.setState({ a: true });
this.setState({ b: true });
}
}
2. Gibt es jemals die Möglichkeit, dass a falsch und b wahr ist für:
class SuperContainer extends React.Component {
constructor(props) {
super(props);
this.state = { a: false };
}
render() {
return <Container setParentState={this.setState.bind(this)}/>
}
}
class Container extends React.Component {
constructor(props) {
super(props);
this.state = { b: false };
}
render() {
return <Button onClick={this.handleClick}/>
}
handleClick = () => {
this.props.setParentState({ a: true });
this.setState({ b: true });
}
}
Denken Sie daran, dass dies extreme Vereinfachungen meines Anwendungsfalls sind. Mir ist klar, dass ich dies anders machen kann, z. B. indem ich in Beispiel 1 beide Statusparameter gleichzeitig aktualisiere und die zweite Statusaktualisierung in einem Rückruf zur ersten Statusaktualisierung in Beispiel 2 durchführe. Dies ist jedoch nicht meine Frage. und ich bin nur daran interessiert, ob es eine genau definierte Art und Weise gibt, wie React diese Statusaktualisierungen durchführt, sonst nichts.
Jede durch Dokumentation gesicherte Antwort wird sehr geschätzt.
quelle
Antworten:
Ich arbeite an React.
TLDR:
Ja.
Ja.
Die Reihenfolge der Updates wird immer eingehalten. Ob Sie einen Zwischenzustand "zwischen" ihnen sehen oder nicht, hängt davon ab, ob Sie sich in einem Stapel befinden oder nicht.
Derzeit (React 16 und früher) werden standardmäßig nur Aktualisierungen in React-Ereignishandlern gestapelt . Es gibt eine instabile API, um das Batching außerhalb von Ereignishandlern in seltenen Fällen zu erzwingen, wenn Sie es benötigen.
In zukünftigen Versionen (wahrscheinlich React 17 und höher) stapelt React standardmäßig alle Updates, sodass Sie nicht darüber nachdenken müssen. Wie immer werden wir diesbezügliche Änderungen im React-Blog und in den Versionshinweisen bekannt geben.
Der Schlüssel zum Verständnis ist, dass unabhängig davon, wie viele
setState()
Aufrufe in wie vielen Komponenten Sie in einem React-Ereignishandler ausführen , am Ende des Ereignisses nur ein einziges erneutes Rendern erzeugt wird . Dies ist entscheidend für eine gute Leistung in großen Anwendungen , denn wennChild
undParent
jeden Anruf ,setState()
wenn einem Klick Event - Handling, Sie wollen nicht wieder machen dieChild
zweimal.In beiden Beispielen werden
setState()
Aufrufe in einem React-Ereignishandler ausgeführt. Daher werden sie am Ende des Ereignisses immer zusammengespült (und Sie sehen den Zwischenzustand nicht).Die Aktualisierungen werden immer flach in der Reihenfolge zusammengeführt, in der sie auftreten . Wenn also das erste Update ist
{a: 10}
, ist das zweite{b: 20}
und das dritte ist{a: 30}
der gerenderte Zustand{a: 30, b: 20}
. Das neuere Update auf denselben Statusschlüssel (z. B. wiea
in meinem Beispiel) "gewinnt" immer.Das
this.state
Objekt wird aktualisiert, wenn die Benutzeroberfläche am Ende des Stapels erneut gerendert wird. Wenn Sie also den Status basierend auf einem vorherigen Status aktualisieren müssen (z. B. das Inkrementieren eines Zählers), sollten Sie die Funktionsversion verwendensetState(fn)
, die den vorherigen Status angibt, anstatt aus zu lesenthis.state
. Wenn Sie neugierig auf die Gründe dafür sind, habe ich dies in diesem Kommentar ausführlich erläutert .In Ihrem Beispiel wird der "Zwischenstatus" nicht angezeigt, da wir uns in einem React-Ereignishandler befinden, in dem die Stapelverarbeitung aktiviert ist (weil React "weiß", wann wir dieses Ereignis beenden).
Sowohl in React 16 als auch in früheren Versionen gibt es jedoch außerhalb der React-Ereignishandler standardmäßig noch keine Stapelverarbeitung . Wenn wir in Ihrem Beispiel stattdessen einen AJAX-Antworthandler hätten
handleClick
,setState()
würde jeder sofort verarbeitet, sobald dies geschieht. In diesem Fall ja, man würde einen Zwischenzustand sehen:Wir wissen, dass es unpraktisch ist, dass das Verhalten unterschiedlich ist, je nachdem, ob Sie sich in einem Event-Handler befinden oder nicht . Dies wird sich in einer zukünftigen React-Version ändern, die standardmäßig alle Updates stapelt (und eine Opt-In-API bereitstellt, um Änderungen synchron zu löschen). Bis wir das Standardverhalten ändern (möglicherweise in React 17), gibt es eine API, mit der Sie das Batching erzwingen können :
Intern React-Ereignishandler werden alle eingeschlossen,
unstable_batchedUpdates
weshalb sie standardmäßig gestapelt sind. Beachten Sie, dass dasunstable_batchedUpdates
zweimalige Umschließen eines Updates keine Auswirkungen hat. Die Aktualisierungen werden gelöscht, wenn wir den äußerstenunstable_batchedUpdates
Anruf beenden .Diese API ist "instabil" in dem Sinne, dass wir sie entfernen, wenn die Stapelverarbeitung bereits standardmäßig aktiviert ist. Wir werden es jedoch in einer Nebenversion nicht entfernen, sodass Sie sich bis React 17 sicher darauf verlassen können, wenn Sie in einigen Fällen das Stapeln außerhalb der React-Ereignishandler erzwingen müssen.
Zusammenfassend ist dies ein verwirrendes Thema, da standardmäßig nur Stapel in Ereignishandlern reagieren. Dies wird sich in zukünftigen Versionen ändern, und das Verhalten wird dann einfacher sein. Die Lösung besteht jedoch nicht darin, weniger zu stapeln , sondern standardmäßig mehr zu stapeln. Das werden wir tun.
quelle
obj.a = true; obj.b = true
) und am Ende einfach zu tunthis.setState(obj)
. Dies ist sicher, unabhängig davon, ob Sie sich in einem Ereignishandler befinden oder nicht. Könnte ein guter Trick sein, wenn Sie häufig den Fehler machen, den Status mehrmals außerhalb von Ereignishandlern festzulegen.componentDidUpdate
und andere Lifecycle-Rückrufe ab React 16 enthält? Danke im Voraus!Dies ist eigentlich eine ziemlich interessante Frage, aber die Antwort sollte nicht zu kompliziert sein. Es gibt diesen großartigen Artikel auf Medium , der eine Antwort hat.
1) Wenn Sie dies tun
Ich denke nicht, dass es eine Situation geben
a
wird, in der es aufgrund von Chargen sein wirdtrue
und seinb
wird .false
Allerdings , wenn
b
davon abhängig ista
ja dann könnte es eine Situation, wo Sie den erwarteten Zustand nicht erhalten würden.Nachdem alle oben genannten Anrufe bearbeitet wurden
this.state.value
, ist 1, nicht 3, wie Sie es erwarten würden.Dies wird im Artikel erwähnt:
setState accepts a function as its parameter
Das wird uns geben
this.state.value === 3
quelle
this.state.value
sowohl in Ereignishandlern (wosetState
wird gestapelt) als auch in AJAX-Rückrufen (wosetState
wird nicht gestapelt) aktualisiert wird. In Ereignishandlern würde ich das verwenden,updater function
um immer sicher zu sein, dass ich den Status unter Verwendung des aktuell aktualisierten Status aktualisiere, der von der Funktion bereitgestellt wird. Sollte ichsetState
eine Updater-Funktion im Code des AJAX-Rückrufs verwenden, auch wenn ich weiß, dass dieser nicht gestapelt ist? Könnten Sie bitte die VerwendungsetState
innerhalb eines AJAX-Rückrufs mit oder ohne Verwendung einer Updater-Funktion klären? Danke dir!setState
würden ausgeführt, aber der letzte würde gewinnen.Mehrere Anrufe während desselben Zyklus können zusammen gestapelt werden. Wenn Sie beispielsweise versuchen, eine Artikelmenge mehr als einmal im selben Zyklus zu erhöhen, ergibt dies Folgendes:
https://reactjs.org/docs/react-component.html
quelle
wie in doc
Die Änderung wird wie in der Warteschlange ( FIFO : First In First Out) durchgeführt. Der erste Aufruf erfolgt zuerst
quelle