Perché chiamare il metodo reazioni setState non muta immediatamente lo stato?


326

Sto leggendo la sezione Forms didocumentazione e ho appena provato questo codice per dimostrare l' onChangeutilizzo ( 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')
);

Quando aggiorno il <input/>valore nel browser, il secondo console.logall'interno del handleChangecallback stampa lo stesso valuedel primo console.log, Perché non riesco a vedere il risultato di this.setState({value: event.target.value})nell'ambito del handleChangecallback?


Risposte:


615

Dalla documentazione di React :

setState()non muta immediatamente this.statema crea una transizione di stato in sospeso. L'accesso this.statedopo aver chiamato questo metodo può potenzialmente restituire il valore esistente. Non vi è alcuna garanzia di funzionamento sincrono delle chiamate setStatee le chiamate possono essere messe in batch per ottenere miglioramenti delle prestazioni.

Se si desidera eseguire una funzione dopo il cambio di stato, passarla come callback.

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

Buona risposta. L'osservazione che devo fare è stare attenti a usare valueLink. Funziona bene se non è necessario formattare / mascherare l'input.
Dherik,

42
Potresti anche voler dare un'occhiata componentDidUpdate. Verrà chiamato dopo che lo stato è cambiato.
Keysox,

1
Domanda veloce se posso, vedo che una volta passata la funzione di cui abbiamo bisogno come callback a setState, speravo che la funzione sarebbe stata eseguita prima di chiamare render (). Ma vedo che l'ordine è setState () -> render () -> callback di setStates (). è normale? Cosa succede se vogliamo controllare il nostro rendering in base alle cose che facciamo nel callback? shouldComponentUpdate ?
Semuzaboi,

2
La modifica dello stato di un componente attiverà sempre un nuovo rendering a meno che non ci sia un comportamento shouldComponentUpdateche specifichi diversamente. Cosa stai esattamente cercando di fare nel callback a cui stai passando setStateche desideri eseguire prima del nuovo rendering?
Michael Parker,

4
...perché? Qualcuno potrebbe giustificarlo?
JackHasaKeyboard,

49

Come menzionato nella documentazione di React, non vi è alcuna garanzia di setStateessere licenziato in modo sincrono, quindi è console.logpossibile restituire lo stato prima dell'aggiornamento.

Michael Parker menziona il passaggio di una callback all'interno del setState. Un altro modo per gestire la logica dopo il cambio di stato è tramite il componentDidUpdatemetodo del ciclo di vita, che è il metodo raccomandato nei documenti React.

In genere, consigliamo invece di utilizzare componentDidUpdate () per tale logica.

Ciò è particolarmente utile in caso di successiva setStateattivazione e si desidera attivare la stessa funzione dopo ogni cambio di stato. Invece di aggiungere un callback a ciascuno setState, è possibile posizionare la funzione all'interno di componentDidUpdate, con una logica specifica all'interno, se necessario.

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

16

Potresti provare a usare ES7 async / await. Ad esempio usando il tuo esempio:

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

In che modo la tua risposta è diversa dall'altra risposta di alta qualità?
tod

9
L'altra risposta riguarda l'utilizzo del callback in setState (). Ho pensato di metterlo qui per coloro ai quali non si applica un caso di callback. Ad esempio, quando ho affrontato questo problema da solo, il mio caso d'uso prevedeva un caso di commutazione sullo stato aggiornato subito dopo averlo impostato. Pertanto, è stato preferito utilizzare asincrono / waitit rispetto a un callback.
Kurokiiru,

questo impatto sulle prestazioni se uso sempre attendo sempre quando voglio aggiornare un certo stato e quindi aspetto che sia aggiornato? E se inserisco più setStates in attesa in una catena uno sotto l'altro, verrà visualizzato dopo ogni aggiornamento setState? o dopo l'ultimo aggiornamento di setState?
user2774480


Su ciò che stai chiedendo all'utente2774480, credo che tutto dipende da un caso d'uso specifico per determinare quale implementazione utilizzare. Se in una catena vengono utilizzati più setState, le prestazioni ne risentiranno e, sì, verranno visualizzate dopo ogni setState, ma correggimi se sbaglio.
Kurokiiru,


-1

async-await la sintassi funziona perfettamente per qualcosa di simile al seguente ...

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}`);
}

-1

In parole semplici: this.setState ({data: value}) è di natura asincrona, il che significa che si sposta dallo stack di chiamate e ritorna allo stack di chiamate solo se non viene risolto.

Si prega di leggere Event Loop per avere un quadro chiaro della natura asincrona in JS e perché ci vuole tempo per l'aggiornamento -

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

Quindi -

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

come ci vuole tempo per l'aggiornamento. Per ottenere il processo sopra descritto -

    this.setState({data:value},function () {
     console.log(this.state.data);
    });
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.