Warum mutiert der Aufruf der Methode react setState den Status nicht sofort?

326

Ich lese den Abschnitt Formulare vonDokumentation und habe gerade diesen Code ausprobiert, um die onChangeVerwendung zu demonstrieren ( JSBIN ).

var React= require('react');

var ControlledForm= React.createClass({
    getInitialState: function() {
        return {
            value: "initial value"
        };
    },

    handleChange: function(event) {
        console.log(this.state.value);
        this.setState({value: event.target.value});
        console.log(this.state.value);

    },

    render: function() {
        return (
            <input type="text" value={this.state.value} onChange={this.handleChange}/>
        );
    }
});

React.render(
    <ControlledForm/>,
  document.getElementById('mount')
);

Wenn ich die Aktualisierung <input/>Wert im Browser, die zweite console.loginnerhalb des handleChangedruckt Rückruf der gleiche valuewie der erste console.log, warum ich nicht das Ergebnis sehen können , this.setState({value: event.target.value})im Rahmen des handleChangeRückruf?

Tarrsalah
quelle
Wenn Sie Hooks verwenden, werfen Sie einen Blick auf die useState-Set-Methode, die Änderungen nicht sofort widerspiegelt .
Emile Bergeron

Antworten:

615

Aus der Dokumentation von React :

setState()mutiert nicht sofort, this.statesondern erzeugt einen ausstehenden Zustandsübergang. Der Zugriff this.statenach dem Aufrufen dieser Methode kann möglicherweise den vorhandenen Wert zurückgeben. Es gibt keine Garantie für den synchronen Betrieb von Anrufen an setStateund Anrufe können für Leistungssteigerungen gestapelt werden.

Wenn Sie möchten, dass eine Funktion nach der Statusänderung ausgeführt wird, übergeben Sie sie als Rückruf.

this.setState({value: event.target.value}, function () {
    console.log(this.state.value);
});
Michael Parker
quelle
Gute Antwort. Die Beobachtung, die ich machen muss, ist vorsichtig zu sein, valueLink zu verwenden. Es funktioniert gut, wenn Sie die Eingabe nicht formatieren / maskieren müssen.
Dherik
42
Vielleicht möchten Sie auch auschecken componentDidUpdate. Es wird aufgerufen, nachdem sich der Status geändert hat.
Keysox
1
Kurze Frage, wenn ich darf, ich sehe, sobald wir die Funktion übergeben, die wir als Rückruf an setState benötigen, hatte ich gehofft, dass die Funktion zuerst ausgeführt wird, bevor render () aufgerufen wird. Aber ich sehe, die Reihenfolge ist setState () -> render () -> setStates 'callback (). ist das normal? Was ist, wenn wir unser Rendering basierend auf den Aktionen steuern möchten, die wir im Rückruf ausführen? shouldComponentUpdate ?
Semuzaboi
2
Das Ändern des Status einer Komponente löst immer ein erneutes Rendern aus, sofern darin kein shouldComponentUpdateanderes Verhalten angegeben ist. Was genau versuchen Sie in dem Rückruf zu tun, an den Sie übergeben setState, den Sie vor dem erneuten Rendern ausführen möchten?
Michael Parker
4
...Warum? Könnte jemand das rechtfertigen?
JackHasaKeyboard
49

Wie in der React-Dokumentation erwähnt, gibt es keine Garantie für setStateein synchrones Auslösen. Daher console.logkönnen Sie den Status vor der Aktualisierung zurückgeben.

Michael Parker erwähnt die Rückgabe eines Rückrufs innerhalb der setState. Eine andere Möglichkeit, die Logik nach einer Statusänderung zu handhaben, ist die componentDidUpdateLifecycle-Methode, die in React-Dokumenten empfohlen wird.

Im Allgemeinen empfehlen wir stattdessen die Verwendung von componentDidUpdate () für eine solche Logik.

Dies ist besonders nützlich, wenn möglicherweise mehrere aufeinanderfolgende setStates ausgelöst werden und Sie nach jeder Statusänderung dieselbe Funktion auslösen möchten. Anstatt jedem einen Rückruf hinzuzufügen setState, können Sie die Funktion innerhalb von platzieren componentDidUpdate, wobei bei Bedarf eine bestimmte Logik enthalten ist.

// example
componentDidUpdate(prevProps, prevState) {
  if (this.state.value > prevState.value) {
    this.foo();  
  }
}
Yo Wakita
quelle
16

Sie können versuchen, ES7 async / await zu verwenden. Zum Beispiel anhand Ihres Beispiels:

handleChange: async function(event) {
    console.log(this.state.value);
    await this.setState({value: event.target.value});
    console.log(this.state.value);
}
kurokiiru
quelle
Wie unterscheidet sich Ihre Antwort von der anderen hochwertigen Antwort?
tod
9
Die andere Antwort bezieht sich auf die Verwendung des Rückrufs in setState (). Ich dachte, ich würde dies hier für diejenigen einfügen, für die ein Rückruf-Anwendungsfall nicht gilt. Als ich zum Beispiel selbst mit diesem Problem konfrontiert war, umfasste mein Anwendungsfall einen Switch-Fall für den aktualisierten Status direkt nach dem Einstellen. Daher wurde die Verwendung von async / await der Verwendung eines Rückrufs vorgezogen.
Kurokiiru
Wird sich dies auf die Leistung auswirken, wenn ich immer "Warten" verwende, wenn ich einen Status aktualisieren möchte, und dann darauf warte, dass er aktualisiert wird? Und wenn ich mehrere warte setStates in einer Kette untereinander platziere, wird es nach jedem setState-Update gerendert? oder nach dem letzten setState Update?
user2774480
Ich bin der Meinung, dass es bei den Fragen, die Sie an user2774480 stellen, auf einen bestimmten Anwendungsfall ankommt, um zu bestimmen, welche Implementierung verwendet werden soll. Wenn mehrere setStates in einer Kette verwendet werden, wird die Leistung beeinträchtigt, und ja, sie wird nach jedem setState gerendert, aber korrigieren Sie mich, wenn ich falsch liege.
Kurokiiru
-1

async-await Die Syntax funktioniert perfekt für Folgendes:

changeStateFunction = () => {
  // Some Worker..

  this.setState((prevState) => ({
  year: funcHandleYear(),
  month: funcHandleMonth()
}));

goNextMonth = async () => {
  await this.changeStateFunction();
  const history = createBrowserHistory();
  history.push(`/calendar?year=${this.state.year}&month=${this.state.month}`);
}

goPrevMonth = async () => {
  await this.changeStateFunction();
  const history = createBrowserHistory();
  history.push(`/calendar?year=${this.state.year}&month=${this.state.month}`);
}
Ritwik
quelle
-1

Einfach ausgedrückt - this.setState ({data: value}) ist asynchroner Natur, dh es verlässt den Call Stack und kehrt nur dann zum Call Stack zurück, wenn es aufgelöst wird.

Bitte lesen Sie über Event Loop, um ein klares Bild über die asynchrone Natur in JS zu erhalten und warum die Aktualisierung einige Zeit in Anspruch nimmt.

https://medium.com/front-end-weekly/javascript-event-loop-explained-4cd26af121d4

Daher -

    this.setState({data:value});
    console.log(this.state.data); // will give undefined or unupdated value

da es Zeit braucht, um zu aktualisieren. Um den oben genannten Prozess zu erreichen -

    this.setState({data:value},function () {
     console.log(this.state.data);
    });
Vishal Bisht
quelle