Cosa succede quando si utilizza this.setState più volte nel componente React?


101

Volevo controllare cosa succede quando usi this.setState più volte (2 volte per il bene della discussione). Ho pensato che il componente verrà renderizzato due volte ma a quanto pare è renderizzato solo una volta. Un'altra aspettativa che avevo era che forse la seconda chiamata per setState verrà eseguita sulla prima, ma hai indovinato - ha funzionato bene.

Collegamento a un 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')
);

Come vedrai, ad ogni rendering viene visualizzato un avviso che dice "render".

Hai una spiegazione del motivo per cui ha funzionato correttamente?


2
React è piuttosto intelligente e eseguirà nuovamente il rendering solo quando lo stato richiesto per il rendering viene modificato. Nel tuo metodo di rendering stai solo usando this.state.alex: cosa succede se aggiungi anche un elemento che dipende this.state.value?
Martin Wedvich

1
@MartinWedvich Si romperà nel caso io dipenda da esso. Per questo hai il metodo 'getInitialState' - per impostare i valori predefiniti in modo che la tua app non si interrompa.
alexunder

Risposte:


116

I batch React stato gli aggiornamenti che si verificano nei gestori di eventi e nei metodi del ciclo di vita. Pertanto, se aggiorni lo stato più volte in un <div onClick />gestore, React attenderà che la gestione degli eventi finisca prima di eseguire nuovamente il rendering.

Per essere chiari, questo funziona solo nei gestori di eventi sintetici controllati da React e nei metodi del ciclo di vita. Gli aggiornamenti di stato non vengono raggruppati in batch in AJAX e nei setTimeoutgestori di eventi, ad esempio.


1
Grazie, ha senso, qualche idea di come si fa dietro le quinte?
alexunder

13
Poiché React controlla completamente i gestori di eventi sintetici (come <div onClick />) ei metodi del ciclo di vita dei componenti, sa di poter modificare in modo sicuro il comportamento di setStateper tutta la durata della chiamata agli aggiornamenti dello stato batch e attendere lo svuotamento. In un AJAX o un setTimeoutgestore, al contrario, React non ha modo di sapere quando il nostro gestore ha terminato l'esecuzione. Tecnicamente, React accoda gli aggiornamenti di stato in entrambi i casi, ma scarica immediatamente al di fuori di un gestore controllato da React.
Chris Gaudreau

1
@neurosnap Non credo che i documenti vadano nei dettagli su questo. È menzionato in modo astratto nella sezione Stato e ciclo di vita e nei documenti setState . Vedi il codice per ReactDefaultBatchingStrategy , che è attualmente l'unica strategia di batch ufficiale.
Chris Gaudreau

2
@BenjaminToueg Credo che tu possa usare ReactDOM.unstable_batchedUpdates(function)per batch setStateal di fuori dei gestori di eventi React. Come suggerisce il nome, annullerai la garanzia.
Chris Gaudreau

1
sempre bene fare riferimento a questo ragazzo twitter.com/dan_abramov/status/887963264335872000?lang=en
Hertzel Guinness

33

Il metodo setState () non aggiorna immediatamente lo stato del componente, ma mette semplicemente l'aggiornamento in una coda per essere elaborato in seguito. React può raggruppare più richieste di aggiornamento insieme per rendere il rendering più efficiente. Per questo motivo, è necessario adottare precauzioni speciali quando si tenta di aggiornare lo stato in base allo stato precedente del componente.

Ad esempio, il codice seguente incrementerà solo di 1 l'attributo del valore di stato anche se è stato chiamato 4 volte:

 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>
   }
}

Per utilizzare uno stato dopo che è stato aggiornato, eseguire tutta la logica nell'argomento callback:

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

Il metodo setState (updater, [callback]) può accettare una funzione di aggiornamento come primo argomento per aggiornare lo stato in base allo stato e alle proprietà precedenti. Il valore restituito dalla funzione di aggiornamento verrà leggermente unito allo stato del componente precedente. Il metodo aggiorna lo stato in modo asincrono, quindi esiste un'opzione callback che verrà chiamata una volta che lo stato avrà terminato l'aggiornamento completo.

Esempio:

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

Ecco un esempio di come aggiornare lo stato in base allo stato precedente:

    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>
     }
  }

Ah, non sapevo del setState(updater,[callback]), è come un meno prolissoObject.assign ringraziamento per questo!
AJ Venturella

1
@Biboswan, ciao, sono nuovo di React e volevo chiederti che è vero che tutti e quattro i setState all'interno del metodo CompountDidMount sono uniti e SOLO L'ULTIMO setState verrà eseguito ma gli altri 3 verranno ignorati?
Dickens
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.