Warum ist setState in reactjs Async anstelle von Sync?

126

Ich habe gerade festgestellt, dass die React- this.setState()Funktion in einer Komponente asynchron ist oder nach Abschluss der aufgerufenen Funktion aufgerufen wird.

Jetzt habe ich diesen Blog gesucht und gefunden ( setState () State Mutation Operation kann in ReactJS synchron sein )

Hier stellte er fest, dass dies setStateasynchron (aufgerufen, wenn der Stapel leer ist) oder synchron (aufgerufen, sobald aufgerufen) ist, je nachdem, wie die Zustandsänderung ausgelöst wurde.

Nun sind diese beiden Dinge schwer zu verdauen

  1. Im Blog wird die setStateFunktion innerhalb einer Funktion aufgerufen updateState, aber was die updateStateFunktion ausgelöst hat, weiß eine aufgerufene Funktion nicht.
  2. Warum sollten sie setStateasynchron machen, da JS eine Single-Threaded-Sprache ist und dieser setState kein WebAPI- oder Server-Aufruf ist, sondern nur für den JS-Thread ausgeführt werden muss. Tun sie dies so, dass durch das erneute Rendern nicht alle Ereignis-Listener und andere Dinge gestoppt werden, oder gibt es ein anderes Designproblem?
Anup
quelle
1
Ich habe heute einen Artikel geschrieben, der hilft, ein bisschen das Klima in der Umgebung zu beschreiben setState: medium.com/@agm1984/…
agm1984

Antworten:

155

Sie können eine Funktion aufrufen, nachdem der Statuswert aktualisiert wurde:

this.setState({foo: 'bar'}, () => { 
    // Do something here. 
});

Wenn Sie viele Status gleichzeitig aktualisieren müssen, gruppieren Sie sie alle innerhalb desselben setState:

Anstatt:

this.setState({foo: "one"}, () => {
    this.setState({bar: "two"});
});

Mach das einfach:

this.setState({
    foo: "one",
    bar: "two"
});
JoeTidee
quelle
16
ya das ist ok wir haben eine callBack Funktion die wir benutzen können aber dats nicht die Frage.
Anup
12
Hoffentlich hilft es jemand anderem, wie er über diese Frage stolpert.
JoeTidee
2
ya dat kann hilfreich sein
Anup
97

1) setStateAktionen sind asynchron und werden zur Leistungssteigerung gestapelt. Dies wird in der Dokumentation von erläutert setState.

setState () mutiert this.state nicht sofort, sondern erstellt einen ausstehenden Statusübergang. Der Zugriff auf this.state nach dem Aufrufen dieser Methode kann möglicherweise den vorhandenen Wert zurückgeben. Es gibt keine Garantie für den synchronen Betrieb von Aufrufen an setState, und Anrufe können zur Leistungssteigerung gestapelt werden.


2) Warum sollten sie setState asynchronisieren, da JS eine einzelne Thread-Sprache ist und dies setStatekein WebAPI- oder Server-Aufruf ist?

Das ist weil setState sich der Status ändert und ein erneutes Rendern verursacht wird. Dies kann eine teure Operation sein, und wenn der Browser synchronisiert wird, reagiert er möglicherweise nicht mehr.

Daher sind die setState-Aufrufe sowohl asynchron als auch stapelweise, um eine bessere Benutzeroberfläche und Leistung zu erzielen.

Sachin
quelle
59
Wenn Sie die Reihenfolge der Ereignisse nach einem setState-Aufruf sicherstellen müssen, können Sie eine Rückruffunktion übergeben. this.setState({ something: true }, () => console.log(this.state))
Am
1
Danke @Sachin für die Erklärung. Ich habe jedoch immer noch Zweifel, kann es synchron sein, wie der Blog erklärt?
Ajay Gaur
2
Eine weitere dumme Designentscheidung in Reaktion. Machen Sie die Statusaktualisierung synchron und das Rendern asynchron. Sie können Batch-Renderings erstellen, aber ich möchte in der Lage sein, etwas so Primitives wie Statusvariablen festzulegen, ohne sich mit den Rennbedingungen befassen zu müssen.
Ig-Dev
Warum nicht zulassen, dass eine Option festgelegt wird, um die Funktion entweder asynchron oder synchron zu machen? Das wäre eine nützliche Funktion
Randall Coding
Natürlich bin ich nicht im Reaktionsteam, aber ein Grund für die asynchrone Statusaktualisierung ist meiner Meinung nach, dass Browser Single-Threaded sind. Synchronisierungsvorgänge können dazu führen, dass die Benutzeroberfläche nicht mehr reagiert, und sind kein guter Kandidat für die Benutzeroberfläche.
Sachin
16

Ich weiß, dass diese Frage alt ist, aber sie hat viele reaktionsfähige Benutzer, einschließlich mir, lange Zeit sehr verwirrt. Kürzlich hat Dan Abramov (vom Reaktionsteam) eine großartige Erklärung geschrieben, warum die Natur von setStateasynchron ist:

https://github.com/facebook/react/issues/11527#issuecomment-360199710

setStatesoll asynchron sein, und dafür gibt es in der verknüpften Erklärung von Dan Abramov einige wirklich gute Gründe. Dies bedeutet nicht , es wird immer asynchron sein - es bedeutet vor allem , dass man einfach nicht kann abhängen auf wobei synchron . ReactJS berücksichtigt viele Variablen in dem Szenario, in dem Sie den Status ändern, um zu entscheiden, wann die statetatsächlich aktualisiert und Ihre Komponente erneut gerendert werden soll.
Ein einfaches Beispiel, um dies zu demonstrieren, ist, dass wenn Sie setStateals Reaktion auf eine Benutzeraktion aufrufen , statediese wahrscheinlich sofort aktualisiert wird (obwohl Sie sich wiederum nicht darauf verlassen können), sodass der Benutzer keine Verzögerung verspürt , aber wenn du anrufstsetState Als Reaktion auf eine Ajax-Anrufantwort oder ein anderes Ereignis, das nicht vom Benutzer ausgelöst wird, wird der Status möglicherweise mit einer geringfügigen Verzögerung aktualisiert, da der Benutzer diese Verzögerung nicht wirklich spürt und die Leistung durch Warten auf verbessert Stapeln Sie mehrere Statusaktualisierungen zusammen und rendern Sie das DOM weniger oft neu.

gillyb
quelle
Sie haben keine Antwort als die richtige markiert. Die Leute veröffentlichen, wie man es umgehen kann. Nicht die Antwort auf die gestellte Frage. Dieser Artikel scheint gut zu sein.
Anup
@Anup Die Antwort ist etwas komplizierter als nur "asynchron" oder "synchron". Es sollte immer als "asynchron" behandelt werden, kann jedoch in einigen Fällen als "synchron" fungieren. Ich hoffe, ich habe etwas Licht für dich gebracht.
Gillyb
8

Guter Artikel hier https://github.com/vasanthk/react-bits/blob/master/patterns/27.passing-function-to-setState.md

// assuming this.state.count === 0
this.setState({count: this.state.count + 1});
this.setState({count: this.state.count + 1});
this.setState({count: this.state.count + 1});
// this.state.count === 1, not 3

Solution
this.setState((prevState, props) => ({
  count: prevState.count + props.increment
}));

oder Rückruf weitergeben this.setState ({.....},callback)

https://medium.com/javascript-scene/setstate-gate-abc10a9b2d82 https://medium.freecodecamp.org/functional-setstate-is-the-future-of-react-374f30401b6b

zloctb
quelle
7

Sie können den folgenden Wrap verwenden, um einen Synchronisierungsaufruf zu tätigen

this.setState((state =>{
  return{
    something
  }
})

Ярослав
quelle
unterschätzte Antwort
James Cat
1

Stellen Sie sich vor, Sie erhöhen einen Zähler in einer Komponente:

  class SomeComponent extends Component{

    state = {
      updatedByDiv: '',
      updatedByBtn: '',
      counter: 0
    }

    divCountHandler = () => {
      this.setState({
        updatedByDiv: 'Div',
        counter: this.state.counter + 1
      });
      console.log('divCountHandler executed');
    }

    btnCountHandler = () => {
      this.setState({
        updatedByBtn: 'Button',
        counter: this.state.counter + 1
      });
      console.log('btnCountHandler executed');
    }
    ...
    ...
    render(){
      return (
        ...
        // a parent div
        <div onClick={this.divCountHandler}>
          // a child button
          <button onClick={this.btnCountHandler}>Increment Count</button>
        </div>
        ...
      )
    }
  }

Sowohl der übergeordneten als auch der untergeordneten Komponente ist ein Zählhandler zugeordnet. Dies geschieht absichtlich, damit wir setState () zweimal im selben Kontext für das Sprudeln von Klickereignissen ausführen können, jedoch innerhalb von zwei verschiedenen Handlern.

Wie wir uns vorstellen würden, würde ein Ereignis mit einem Klick auf die Schaltfläche nun beide Handler auslösen, da das Ereignis während der Blasenphase vom Ziel zum äußersten Container sprudelt.

Daher wird btnCountHandler () zuerst ausgeführt, wobei erwartet wird, dass die Anzahl auf 1 erhöht wird, und dann wird divCountHandler () ausgeführt, um die Anzahl auf 2 zu erhöhen.

Die Anzahl wird jedoch nur auf 1 erhöht, wie Sie in den React Developer-Tools überprüfen können.

Dies beweist, dass reagieren

  • stellt alle setState-Aufrufe in die Warteschlange

  • kehrt zu dieser Warteschlange zurück, nachdem die letzte Methode im Kontext ausgeführt wurde (in diesem Fall der divCountHandler).

  • führt alle Objektmutationen, die innerhalb mehrerer setState-Aufrufe im selben Kontext auftreten (z. B. alle Methodenaufrufe innerhalb einer einzelnen Ereignisphase, zum selben Kontext), zu einer einzigen Objektmutationssyntax zusammen (Zusammenführung ist sinnvoll, da wir daher die Statuseigenschaften unabhängig voneinander aktualisieren können in setState () an erster Stelle)

  • und übergibt es an ein einzelnes setState (), um ein erneutes Rendern aufgrund mehrerer setState () -Aufrufe zu verhindern (dies ist eine sehr primitive Beschreibung der Stapelverarbeitung).

Resultierender Code, der von react ausgeführt wird:

this.setState({
  updatedByDiv: 'Div',
  updatedByBtn: 'Button',
  counter: this.state.counter + 1
})

Um dieses Verhalten zu stoppen, werden Rückrufe übergeben, anstatt Objekte als Argumente an die setState-Methode zu übergeben.

    divCountHandler = () => {
          this.setState((prevState, props) => {
            return {
              updatedByDiv: 'Div',
              counter: prevState.counter + 1
            };
          });
          console.log('divCountHandler executed');
        }

    btnCountHandler = () => {
          this.setState((prevState, props) => {
            return {
              updatedByBtn: 'Button',
              counter: prevState.counter + 1
            };
          });
      console.log('btnCountHandler executed');
    }

Nachdem die letzte Methode die Ausführung abgeschlossen hat und reag zurückkehrt, um die setState-Warteschlange zu verarbeiten, ruft sie einfach den Rückruf für jede setState-Warteschlange auf und übergibt den vorherigen Komponentenstatus.

Auf diese Weise wird sichergestellt, dass der letzte Rückruf in der Warteschlange den Status aktualisiert, den alle vorherigen Kollegen in die Hand genommen haben.

supi
quelle
0

Ja, setState () ist asynchron.

Über den Link: https://reactjs.org/docs/react-component.html#setstate

  • React garantiert nicht, dass die Statusänderungen sofort angewendet werden.
  • setState () aktualisiert die Komponente nicht immer sofort.
  • Stellen Sie sich setState () als Anforderung und nicht als sofortigen Befehl zum Aktualisieren der Komponente vor.

Weil sie denken Über
den Link: https://github.com/facebook/react/issues/11527#issuecomment-360199710

... wir sind uns einig, dass das synchrone Rendern von setState () in vielen Fällen ineffizient wäre

Asynchrones setState () macht das Leben für diejenigen, die anfangen und leider sogar Erfahrung haben, sehr schwierig:
- Unerwartete Renderprobleme: verzögertes Rendern oder kein Rendern (basierend auf der Programmlogik)
- Übergeben von Parametern ist unter anderem eine große Sache
.

Das folgende Beispiel hat geholfen:

// call doMyTask1 - here we set state
// then after state is updated...
//     call to doMyTask2 to proceed further in program

constructor(props) {
    // ..

    // This binding is necessary to make `this` work in the callback
    this.doMyTask1 = this.doMyTask1.bind(this);
    this.doMyTask2 = this.doMyTask2.bind(this);
}

function doMyTask1(myparam1) {
    // ..

    this.setState(
        {
            mystate1: 'myvalue1',
            mystate2: 'myvalue2'
            // ...
        },    
        () => {
            this.doMyTask2(myparam1); 
        }
    );
}

function doMyTask2(myparam2) {
    // ..
}

Hoffentlich hilft das.

Manohar Reddy Poreddy
quelle