Perché setState in syndjs Async anziché Sync?


126

Ho appena scoperto che nella this.setState()funzione di reazione in qualsiasi componente è asincrono o viene chiamato dopo il completamento della funzione in cui è stato chiamato.

Ora ho cercato e trovato questo blog ( setState () L'operazione di mutazione di stato può essere sincrona in ReactJS )

Qui l'ha trovato setState è asincrono (chiamato quando lo stack è vuoto) o sincronizzato (chiamato non appena chiamato) a seconda di come è stato attivato il cambio di stato.

Ora queste due cose sono difficili da digerire

  1. Nel blog la setStatefunzione è chiamata all'interno di una funzione updateState, ma ciò che ha innescato la updateStatefunzione non è qualcosa che una funzione chiamata dovrebbe conoscere.
  2. Perché dovrebbero essere setStateasincroni poiché JS è un linguaggio a thread singolo e questo setState non è una WebAPI o una chiamata al server, quindi deve essere eseguito solo sul thread di JS. Lo stanno facendo in modo che Re-Rendering non fermi tutti gli ascoltatori e tutto il resto dell'evento, o c'è qualche altro problema di progettazione.


1
Oggi ho scritto un articolo che aiuta a descrivere un po 'del clima intorno setState: medium.com/@agm1984/…
agm1984

Risposte:


155

È possibile chiamare una funzione dopo l'aggiornamento del valore di stato:

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

Inoltre, se hai molti stati da aggiornare contemporaneamente, raggruppali tutti nello stesso setState:

Invece di:

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

Basta fare questo:

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

16
va bene, abbiamo una funzione callback che possiamo usare ma non è una domanda.
Anup,

12
Spero che possa aiutare qualcun altro a imbattersi in questa domanda.
JoeTidee

2
ya dat può essere utile
Anup

97

1) le setStateazioni sono asincrone e vengono raggruppate per ottenere miglioramenti delle prestazioni. Questo è spiegato nella documentazione di setState.

setState () non muta immediatamente this.state ma crea una transizione di stato in sospeso. L'accesso a this.state dopo aver chiamato questo metodo può potenzialmente restituire il valore esistente. Non è garantito il funzionamento sincrono delle chiamate a setState e le chiamate possono essere messe in batch per ottenere miglioramenti delle prestazioni.


2) Perché dovrebbero rendere asincrono setState poiché JS è un linguaggio a thread singolo e setStatenon si tratta di una chiamata WebAPI o server?

Questo perché setStatealtera lo stato e provoca il rinvio. Questa può essere un'operazione costosa e renderla sincrona potrebbe non rispondere al browser.

Pertanto, le chiamate setState sono asincrone e in batch per una migliore esperienza e prestazioni dell'interfaccia utente.


59
Se è necessario garantire l'ordine degli eventi dopo aver effettuato una chiamata setState, è possibile passare una funzione di richiamata. this.setState({ something: true }, () => console.log(this.state))
ianks,

1
Grazie @Sachin per la spiegazione. Tuttavia, ho ancora dei dubbi, può essere sincrono come spiega il blog?
Ajay Gaur,

2
Un'altra stupida decisione progettuale in reazione. Rende sincrono l'aggiornamento dello stato e il rendering asincrono. Puoi eseguire il rendering in batch, ma voglio essere in grado di impostare qualcosa di primitivo come le variabili di stato senza dover affrontare le condizioni di gara.
ig-dev,

Perché non consentire l'impostazione di un'opzione per rendere la fucntion asincrona o sincronizzata? Sarebbe una caratteristica utile
Randall Coding,

Ovviamente, non faccio parte del gruppo di reazione, ma a mio avviso un motivo per rendere asincrono gli aggiornamenti di stato è che i browser sono a thread singolo. Le operazioni di sincronizzazione possono rendere l'IU non rispondente e non sono un buon candidato per l'interfaccia utente.
Sachin,

16

So che questa domanda è vecchia, ma ha causato molta confusione per molti utenti di reazioni da molto tempo, incluso me. Recentemente Dan Abramov (dal team di reazione) ha appena scritto una grande spiegazione sul perché la natura di setStateè asincrona:

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

setStateè pensato per essere asincrono, e ci sono alcuni buoni motivi per farlo nella spiegazione collegata di Dan Abramov. Questo non significa che sarà sempre asincrono - significa principalmente che non puoi dipendere dal fatto che sia sincrono . ReactJS prende in considerazione molte variabili nello scenario in cui si sta modificando lo stato, per decidere quando statedevono essere effettivamente aggiornate e il rendering del componente.
Un semplice esempio per dimostrarlo è che se si chiama setStatecome reazione a un'azione dell'utente, allora ilstate probabilmente verrà aggiornato immediatamente (anche se, di nuovo, non si può contare su di esso), quindi l'utente non avvertirà alcun ritardo , ma se chiamisetState in risposta a una risposta alla chiamata Ajax o qualche altro evento che non è attivato dall'utente, lo stato potrebbe essere aggiornato con un leggero ritardo, poiché l'utente non avvertirà davvero questo ritardo e migliorerà le prestazioni aspettando di raggruppare più aggiornamenti di stato insieme e rendere nuovamente il DOM meno volte.


non hai contrassegnato nessun risponditore come quello giusto. Le persone pubblicano come aggirarlo. Non la risposta alla domanda posta. questo articolo sembra buono.
Anup,

@Anup La risposta è un po 'più complicata del semplice "asincrono" o "sincronizzazione". Dovrebbe essere sempre trattato come "asincrono", ma in alcuni casi potrebbe agire come "sincronizzazione". Spero di aver fatto luce per te.
Gillyb,

8

Buon articolo qui 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
}));

o passare la richiamata this.setState ({.....},callback)

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


7

È possibile utilizzare la seguente sequenza per effettuare la chiamata di sincronizzazione

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


risposta sottovalutata
James Cat

1

Immagina di incrementare un contatore in alcuni componenti:

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

Esiste un gestore di conteggio collegato ai componenti padre e figlio. Questo viene fatto di proposito in modo da poter eseguire setState () due volte all'interno dello stesso contesto di bubbling dell'evento click, ma da 2 gestori diversi.

Come immaginiamo, un singolo clic sul pulsante ora attiverebbe entrambi questi gestori poiché l'evento bolle dal target al contenitore più esterno durante la fase di bubbling.

Pertanto btnCountHandler () viene eseguito per primo, si prevede che il conteggio aumenti a 1 e quindi divCountHandler () viene eseguito, si prevede che il conteggio aumenti a 2.

Tuttavia, il conteggio aumenta solo a 1, come è possibile esaminare negli strumenti di React Developer.

Questo dimostra che reagire

  • mette in coda tutte le chiamate setState

  • ritorna su questa coda dopo aver eseguito l'ultimo metodo nel contesto (divCountHandler in questo caso)

  • unisce tutte le mutazioni di oggetto che avvengono all'interno di più chiamate setState nello stesso contesto (tutte le chiamate di metodo all'interno di una singola fase di evento sono lo stesso contesto per esempio) in una sintassi di mutazione di singolo oggetto (l'unione ha senso perché è per questo che possiamo aggiornare le proprietà dello stato in modo indipendente in setState () in primo luogo)

  • e lo passa in un unico setState () per impedire il rendering di nuovo a causa di più chiamate setState () (questa è una descrizione molto primitiva del batch).

Il codice risultante eseguito da reazioni:

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

Per interrompere questo comportamento, invece di passare oggetti come argomenti al metodo setState, i callback vengono passati.

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

Al termine dell'esecuzione dell'ultimo metodo e quando reazioni ritorna per elaborare la coda setState, chiama semplicemente il callback per ogni setState in coda, passando nello stato componente precedente.

In questo modo reagire assicura che l'ultimo callback nella coda riesca ad aggiornare lo stato su cui tutte le sue controparti precedenti hanno messo le mani.


0

Sì, setState () è asincrono.

Dal link: https://reactjs.org/docs/react-component.html#setstate

  • React non garantisce che i cambiamenti di stato vengano applicati immediatamente.
  • setState () non aggiorna sempre immediatamente il componente.
  • Pensa a setState () come una richiesta piuttosto che come un comando immediato per aggiornare il componente.

Perché pensano
Dal link: https://github.com/facebook/react/issues/11527#issuecomment-360199710

... concordiamo sul fatto che il re-rendering in modo sincrono di setState () sarebbe inefficiente in molti casi

SetState () asincrono rende la vita molto difficile per coloro che iniziano e hanno purtroppo avuto esperienza:
- problemi di rendering imprevisti: rendering ritardato o nessun rendering (basato sulla logica del programma)
- passare i parametri è un grosso problema
tra le altre questioni.

L'esempio seguente ha aiutato:

// 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) {
    // ..
}

Spero che aiuti.

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.