React `setState‘ verstehen
React-Komponenten können und tun dies häufig. Status kann alles sein, aber denken Sie an Dinge wie, ob ein Benutzer angemeldet ist oder nicht und zeigt den richtigen Benutzernamen basierend auf dem Konto aktiv ist. Oder eine Reihe von Blog-Posts. Oder ob ein Modal geöffnet ist oder nicht und welche Registerkarte darin aktiv ist.
React-Komponenten mit state rendern die Benutzeroberfläche basierend auf diesem Status. Wenn sich der Status von Komponenten ändert, ändert sich auch die Komponenten-Benutzeroberfläche.
Deshalb ist es wichtig zu verstehen, wann und wie Sie den Status Ihrer Komponente ändern können. Am Ende dieses Tutorials sollten Sie wissen, wie setState
funktioniert, und in der Lage sein, häufige Fallstricke zu vermeiden, auf die viele von uns stoßen, wenn Sie React lernen.
Funktionsweise von `setState()`
setState()
ist die einzige legitime Möglichkeit, den Status nach der anfänglichen Statuseinrichtung zu aktualisieren. Angenommen, wir haben eine Suchkomponente und möchten den Suchbegriff anzeigen, den ein Benutzer eingibt.
Hier ist das Setup:
import React, { Component } from 'react'class Search extends Component { constructor(props) { super(props) state = { searchTerm: '' } }}
Wir übergeben einen leeren String als Wert und um den Status von searchTerm
zu aktualisieren, müssen wir setState()
aufrufen.
setState({ searchTerm: event.target.value })
Hier übergeben wir ein Objekt an setState()
. Das Objekt enthält den Teil des zu aktualisierenden Status, der in diesem Fall der Wert von searchTerm
. React nimmt diesen Wert und fügt ihn in das Objekt ein, das ihn benötigt. Es ist so, als ob die Search
-Komponente fragt, was sie für den Wert von searchTerm
und setState()
verwenden soll antwortet mit einer Antwort.
Dies startet im Grunde einen Prozess, den React Versöhnung nennt. Der Abstimmungsprozess ist die Art und Weise, wie React das DOM aktualisiert, indem Änderungen an der Komponente basierend auf der Statusänderung vorgenommen werden. Wenn die Anforderung an setState()
ausgelöst wird, erstellt React einen neuen Baum, der die reaktiven Elemente in der Komponente enthält (zusammen mit dem aktualisierten Status). Dieser Baum wird verwendet, um herauszufinden, wie sich die Benutzeroberfläche der Search
-Komponente als Reaktion auf die Statusänderung ändern soll, indem sie mit den Elementen des vorherigen Baums verglichen wird. React weiß, welche Änderungen implementiert werden müssen, und aktualisiert die Teile des DOM nur bei Bedarf. Deshalb ist React schnell.
Das klingt nach viel, aber um den Fluss zusammenzufassen:
- Wir haben eine Suchkomponente, die einen Suchbegriff anzeigt
- Dieser Suchbegriff ist derzeit leer
- Der Benutzer gibt einen Suchbegriff ein
- Dieser Begriff wird erfasst und gespeichert von
setState
als Wert - Es findet ein Abgleich statt und React bemerkt die Wertänderung
- React weist die Suchkomponente an, den Wert zu aktualisieren, und der Suchbegriff wird in
Der Abstimmungsprozess ändert nicht unbedingt den gesamten Baum, außer in einer Situation, in der die Wurzel des Baums wie folgt geändert wird:
// old<div> <Search /></div>// new<span> <Search /></span>
Alle <div>
Tags werden zu <span>
Tags und der gesamte Komponentenbaum wird als Ergebnis aktualisiert.
Die Faustregel lautet, den Zustand niemals direkt zu mutieren. Verwenden Sie immer setState()
, um den Status zu ändern. Wenn Sie den Status direkt ändern, wie im folgenden Snippet, wird die Komponente nicht neu gerendert.
// do not do thisthis.state = { searchTerm: event.target.value}
Übergeben einer Funktion an `setState()`
Um diese Idee weiter zu demonstrieren, erstellen wir einen einfachen Zähler, der beim Klicken inkrementiert und dekrementiert.
Siehe den Stift setState Pen von Kingsley Silas Chijioke (@kinsomicrote) auf CodePen.
Registrieren wir die Komponente und definieren das Markup für die Benutzeroberfläche:
class App extends React.Component {state = { count: 0 }handleIncrement = () => { this.setState({ count: this.state.count + 1 })}handleDecrement = () => { this.setState({ count: this.state.count - 1 })} render() { return ( <div> <div> {this.state.count} </div> <button onClick={this.handleIncrement}>Increment by 1</button> <button onClick={this.handleDecrement}>Decrement by 1</button> </div> ) }}
Zu diesem Zeitpunkt erhöht oder dekrementiert der Zähler die Anzahl bei jedem Klick einfach um 1.
Aber was wäre, wenn wir stattdessen um 3 inkrementieren oder dekrementieren wollten? Wir könnten versuchen, setState()
dreimal in den handleDecrement
und handleIncrement
Funktionen wie folgt aufzurufen:
handleIncrement = () => { this.setState({ count: this.state.count + 1 }) this.setState({ count: this.state.count + 1 }) this.setState({ count: this.state.count + 1 })}handleDecrement = () => { this.setState({ count: this.state.count - 1 }) this.setState({ count: this.state.count - 1 }) this.setState({ count: this.state.count - 1 })}
Wenn Sie zu Hause programmieren, werden Sie vielleicht überrascht sein, dass das nicht funktioniert.
Das obige Code-Snippet entspricht:
Object.assign( {}, { count: this.state.count + 1 }, { count: this.state.count + 1 }, { count: this.state.count + 1 },)
Object.assign()
wird verwendet, um Daten von einem Quellobjekt in ein Zielobjekt zu kopieren. Wenn die Daten, die von der Quelle zum Ziel kopiert werden, alle dieselben Schlüssel haben, wie in unserem Beispiel, gewinnt das letzte Objekt. Hier ist eine einfachere Version davon, wie Object.assign()
funktioniert;
let count = 3const object = Object.assign({}, {count: count + 1}, {count: count + 2}, {count: count + 3});console.log(object);// output: Object { count: 6 }
Anstatt dass der Anruf dreimal stattfindet, passiert er nur einmal. Dies kann behoben werden, indem eine Funktion an setState()
. So wie Sie Objekte an setState()
, können Sie auch Funktionen übergeben, und das ist der Ausweg aus der obigen Situation.
Wenn wir die handleIncrement
-Funktion so bearbeiten, dass sie so aussieht:
handleIncrement = () => { this.setState((prevState) => ({ count: prevState.count + 1 })) this.setState((prevState) => ({ count: prevState.count + 1 })) this.setState((prevState) => ({ count: prevState.count + 1 }))}
… können wir die Anzahl jetzt mit einem Klick dreimal erhöhen.
In diesem Fall stellt React die Funktionsaufrufe in der Reihenfolge, in der sie ausgeführt werden, in die Warteschlange, anstatt sie zusammenzuführen, und aktualisiert den gesamten Status, sobald er abgeschlossen ist. Dadurch wird der Status von count auf 3 anstelle von 1 aktualisiert.
Zugriff auf vorherigen Status mit Updater
Beim Erstellen von React-Anwendungen gibt es Zeiten, in denen Sie den Status basierend auf dem vorherigen Status der Komponente berechnen möchten. Sie können nicht immer darauf vertrauen, dass this.state
sofort nach dem Aufruf von setState()
den richtigen Status beibehält, da er immer dem auf dem Bildschirm gerenderten Status entspricht.
Gehen wir zurück zu unserem Gegenbeispiel, um zu sehen, wie das funktioniert. Angenommen, wir haben eine Funktion, die unsere Zählung um 1 dekrementiert. Diese Funktion sieht so aus:
changeCount = () => { this.setState({ count: this.state.count - 1})}
Was wir wollen, ist die Fähigkeit, um 3 zu dekrementieren. Die changeCount()
Funktion wird dreimal in einer Funktion aufgerufen, die das Klickereignis wie folgt behandelt.
handleDecrement = () => { this.changeCount() this.changeCount() this.changeCount()}
Jedes Mal, wenn auf die Schaltfläche zum Dekrementieren geklickt wird, verringert sich die Anzahl um 1 anstelle von 3. Dies liegt daran, dass this.state.count
erst aktualisiert wird, wenn die Komponente neu gerendert wurde. Die Lösung besteht darin, einen Updater zu verwenden. Mit einem Updater können Sie auf den aktuellen Status zugreifen und ihn sofort zum Aktualisieren anderer Elemente verwenden. Die changeCount()
Funktion wird also so aussehen.
changeCount = () => { this.setState((prevState) => { return { count: prevState.count - 1} })}
Jetzt sind wir nicht mehr abhängig vom Ergebnis von this.state
. Die Zustände von count
sind aufeinander aufgebaut, so dass wir auf den richtigen Zustand zugreifen können, der sich bei jedem Aufruf von changeCount()
ändert.
setState()
sollte asynchron behandelt werden — mit anderen Worten, erwarten Sie nicht immer, dass sich der Status nach dem Aufruf von setState()
geändert hat.
Einpacken
Wenn Sie mit setState()
arbeiten, sind dies die wichtigsten Dinge, die Sie wissen sollten:
- Das Aktualisieren auf einen Komponentenstatus sollte mit
setState()
- Sie können ein Objekt oder eine Funktion an
setState()
- Übergeben Sie eine Funktion, wenn Sie den Status mehrmals aktualisieren können
- Hängen Sie nicht davon ab.geben Sie sofort nach dem Aufruf von
setState()
an und verwenden Sie stattdessen die Updater-Funktion.