Was passiert, wenn this.setState in der React-Komponente mehrmals verwendet wird?

99

Ich wollte überprüfen, was passiert, wenn Sie this.setState mehrmals verwenden (2 Mal für die Diskussion). Ich dachte, dass die Komponente zweimal gerendert wird, aber anscheinend wird sie nur einmal gerendert. Eine andere Erwartung, die ich hatte, war, dass vielleicht der zweite Aufruf von setState über den ersten läuft, aber Sie haben es erraten - es hat gut funktioniert.

Link zu einer JSfiddle

var Hello = React.createClass({
  render: function() {
    return (
      <div>
        <div>Hello {this.props.name}</div>
        <CheckBox />
      </div>
    );
  }
});

var CheckBox = React.createClass({
  getInitialState: function() {
    return {
      alex: 0
    };
  },

  handleChange: function(event) {
    this.setState({
      value: event.target.value
    });
    this.setState({
      alex: 5
    });
  },

  render: function() {
    alert('render');
    return (
      <div>
        <label htmlFor="alex">Alex</label>
        <input type="checkbox" onChange={this.handleChange} name="alex" />
        <div>{this.state.alex}</div>
      </div>
    );
  }
});

ReactDOM.render(
  <Hello name="World" />,
  document.getElementById('container')
);

Wie Sie sehen werden, wird bei jedem Rendern eine Warnung mit der Aufschrift "Rendern" angezeigt.

Haben Sie eine Erklärung, warum es richtig funktioniert hat?

Alexunder
quelle
2
React ist ziemlich clever und wird nur dann neu gerendert, wenn der zum Rendern erforderliche Status geändert wird. In Ihrer Rendermethode verwenden Sie nur this.state.alex- was passiert, wenn Sie ein Element hinzufügen, das ebenfalls davon abhängt this.state.value?
Martin Wedvich
1
@MartinWedvich Es wird kaputt gehen, falls ich davon abhängig bin. Dafür haben Sie die Methode 'getInitialState', mit der Sie die Standardwerte festlegen können, damit Ihre App nicht kaputt geht.
Alexunder
1
verwandt und nützlich: stackoverflow.com/questions/48563650/…
andydavies

Antworten:

115

Reagieren Sie auf Statusaktualisierungen von Stapeln, die in Ereignishandlern und Lebenszyklusmethoden auftreten. Wenn Sie den Status in einem <div onClick />Handler mehrmals aktualisieren , wartet React vor dem erneuten Rendern, bis die Ereignisbehandlung abgeschlossen ist.

Dies funktioniert nur in reaktionsgesteuerten synthetischen Ereignishandlern und Lebenszyklusmethoden. Statusaktualisierungen werden beispielsweise in AJAX- und setTimeoutEreignishandlern nicht gestapelt .

Chris Gaudreau
quelle
1
Danke, es macht Sinn, eine Idee, wie es hinter den Kulissen gemacht wird?
Alexunder
13
Da React synthetische Ereignishandler (wie <div onClick />) und Komponentenlebenszyklusmethoden vollständig steuert , weiß es, dass es das Verhalten von setStatefür die Dauer des Aufrufs sicher ändern kann, um Stapelstatusaktualisierungen durchzuführen und auf das Löschen zu warten. In einem AJAX oder setTimeoutHandler hingegen kann React nicht wissen, wann unser Handler die Ausführung beendet hat. Technisch gesehen werden die React-Warteschlangen in beiden Fällen aktualisiert, jedoch sofort außerhalb eines React-gesteuerten Handlers geleert.
Chris Gaudreau
1
@neurosnap Ich glaube nicht, dass die Dokumente diesbezüglich sehr detailliert sind. Es wird abstrakt im Abschnitt State and Lifecycle und in den setState-Dokumenten erwähnt . Siehe den Code für ReactDefaultBatchingStrategy , die derzeit die einzige offizielle Batching-Strategie ist.
Chris Gaudreau
2
@BenjaminToueg Ich glaube, Sie können ReactDOM.unstable_batchedUpdates(function)damit setStateaußerhalb von React-Ereignishandlern stapeln. Wie der Name schon sagt, erlischt Ihre Garantie.
Chris Gaudreau
1
Es ist immer gut, sich an diesen Typen zu wenden. twitter.com/dan_abramov/status/887963264335872000?lang=de
Hertzel Guinness
32

Die setState () -Methode aktualisiert den Status der Komponente nicht sofort, sondern stellt die Aktualisierung lediglich in eine Warteschlange, um sie später zu verarbeiten. React kann mehrere Aktualisierungsanforderungen stapeln, um das Rendern effizienter zu gestalten. Aus diesem Grund müssen besondere Vorsichtsmaßnahmen getroffen werden, wenn Sie versuchen, den Status basierend auf dem vorherigen Status der Komponente zu aktualisieren.

Der folgende Code erhöht beispielsweise das Statuswertattribut nur um 1, obwohl es viermal aufgerufen wurde:

 class Counter extends React.Component{
   constructor(props){
     super(props)
    //initial state set up
     this.state = {value:0}
   }
   componentDidMount(){
    //updating state
    this.setState({value:this.state.value+1})
    this.setState({value:this.state.value+1})
    this.setState({value:this.state.value+1})
    this.setState({value:this.state.value+1})
   }
   render(){
    return <div>Message:{this.state.value}</div>
   }
}

Um einen Status nach seiner Aktualisierung zu verwenden, führen Sie die gesamte Logik im Rückrufargument aus:

//this.state.count is originally 0
this.setState({count:42}, () => {
  console.log(this.state.count)
//outputs 42
})

Die Methode setState (updater, [callback]) kann eine Updater-Funktion als erstes Argument verwenden, um den Status basierend auf dem vorherigen Status und den vorherigen Eigenschaften zu aktualisieren. Der Rückgabewert der Updater-Funktion wird flach mit dem vorherigen Komponentenstatus zusammengeführt. Die Methode aktualisiert den Status asynchron, sodass es einen Optionsrückruf gibt, der aufgerufen wird, sobald der Status vollständig aktualisiert wurde.

Beispiel:

this.setState((prevState, props) => { 
return {attribute:"value"}
})

Hier ist ein Beispiel für das Aktualisieren des Status basierend auf dem vorherigen Status:

    class Counter extends React.Component{
      constructor(props) {
        super(props)
    //initial state set up
        this.state = {message:"initial message"}
    }
      componentDidMount() {
    //updating state
        this.setState((prevState, props) => {
          return {message: prevState.message + '!'}
        })
     }
     render(){
       return <div>Message:{this.state.message}</div>
     }
  }
Biboswan
quelle
Ah, ich wusste nichts über das setState(updater,[callback]), es ist wie ein weniger ausführlicher Object.assignDank dafür!
AJ Venturella
1
@Biboswan, hi, ich bin neu bei React und wollte Sie fragen, ob es wahr ist, dass alle vier setStates in der CompountDidMount-Methode zusammengeführt werden und NUR DER LETZTE setState ausgeführt wird, die anderen 3 jedoch ignoriert werden.
Dickens