2016-03-18 13 views
56

ho appena scoperto che a reagire this.setState() in qualsiasi componente è asincrona o è chiamato dopo il completamento della funzione che è stato chiamato in.setState in reactjs è asincrona o Sync

Ora ho cercato e trovato questo blog (http://www.bennadel.com/blog/2893-setstate-state-mutation-operation-may-be-synchronous-in-reactjs.htm)

Qui ha trovato che setState è asincrono (chiamato quando stack è vuoto) o sync (chiamato appena chiamato) a seconda di come è stato attivato il cambio di stato.

Ora queste due cose sono difficili da digerire

  1. Nel blog la funzione setState viene chiamata all'interno di una funzione di updateState ora che cosa ha innescato la funzione updateState non è qualcosa che un chiamata funzione può conoscere.
  2. Perché dovrebbero rendere setState async come JS è un linguaggio a thread singolo e questo setState non è una chiamata WebAPI o server, quindi deve essere fatto solo sul thread di JS. Ora se lo stanno facendo in modo che il Re-Rendering non si fermi tutti gli ascoltatori dell'evento e cose o c'è qualche altro problema di design.
+1

Vedere http://stackoverflow.com/questions/28922275/in-reactjs-why-does-setstate-behave-differently-when-called-synchronous – lux

risposta

43

1.) Le azioni setState sono asincrone e sono in batch per i guadagni in termini di prestazioni. Questo è spiegato nella documentazione di setState.

setState() non muta immediatamente questo.stato 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 esiste alcuna garanzia di funzionamento sincrono delle chiamate su setState e le chiamate possono essere raggruppate per ottenere miglioramenti delle prestazioni.


2.) perché avrebbero dovuto fare setState asincrona come JS è sola lingua filettato e questo setState non è un WebAPI o server di chiamata:

Questo perché setState altera lo stato e le cause rerendering. Può essere un'operazione costosa e renderla sincrona potrebbe non rispondere al browser.
Così le chiamate setState sono asincrone e in batch per una migliore esperienza e prestazioni dell'interfaccia utente.

+33

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

+0

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

80

È possibile chiamare una funzione, dopo il valore dello stato ha aggiornato:

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

Inoltre, se hai un sacco di stati di aggiornare in una sola volta, tutti di gruppo all'interno della stessa setState:

Instead of: 

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


just do this: 

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

ya okay, abbiamo una funzione callBack che possiamo usare, ma non la domanda. – Anup

+6

Speriamo che possa aiutare qualcun altro a inciampare in questa domanda. – JoeTidee

+0

ya dat può essere utile – Anup

0

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 chiamata torna this.setState ({.....},callback)

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

2

So che questa domanda è vecchio, ma ha causato un sacco di confusione per molti utenti reactjs per lungo tempo, me compreso. Recentemente Dan Abramov (da parte del team di reagire) appena scritto una grande spiegazione sul motivo per cui la natura della setState è asincrona:

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

setState è destinato a essere asincrona, e loro sono alcuni veramente buoni motivi per quello nella spiegazione collegata di Dan Abramov. Questo non significa che sarà sempre essere asincrono - significa principalmente che non puoi dipendere dal fatto che sia sincrono. ReactJS prende in considerazione molte variabili nello scenario in cui stai cambiando lo stato, per decidere quando lo 'stato' dovrebbe essere effettivamente aggiornato e il tuo componente rerenderizzato.
Un semplice esempio per dimostrarlo, è che se si chiama setState come reazione a un'azione dell'utente, lo stato verrà probabilmente aggiornato immediatamente (anche se, di nuovo, non si può contare su di esso), quindi l'utente ha vinto " t ritardi, ma se chiami setState in risposta a una risposta alle chiamate ajax o ad altri eventi non attivati ​​dall'utente, lo stato potrebbe essere aggiornato con un leggero ritardo, poiché l'utente non sentirà veramente questo ritardo, e migliorerà le prestazioni aspettando di mettere in batch più aggiornamenti di stato insieme e rerenderizzando il DOM un minor numero di volte.

+0

ya non hanno segnato alcun risponditore come quello giusto. Le persone pubblicano come aggirarlo. Non è la risposta alla domanda posta. questo articolo sembra buono. – Anup

+0

@Anup La risposta è un po 'più complicata della semplice' asincrona 'o' sincronizzazione '. Dovrebbe essere sempre considerato come "asincrono", ma in alcuni casi potrebbe agire come "sincronizzazione". Spero di fare un po 'di luce per te. – gillyb

0

Immaginate incrementare un contatore in qualche componente:

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

C'è un gestore di conteggio collegato sia al genitore e i componenti figlio. Questo è fatto apposta per poter eseguire setState() due volte all'interno dello stesso contesto di bubbling di un evento click, ma da 2 diversi gestori.

Come si potrebbe immaginare, un evento con un solo clic sul pulsante attiverà ora entrambi questi gestori poiché le bolle di eventi dal target al contenitore più esterno durante la fase di bubbling.

Quindi il btnCountHandler() eseguito per primo, prevede di incrementare il conteggio a 1 e poi la divCountHandler() esegue, prevede di incrementare il conteggio a 2.

Tuttavia il conteggio incrementa solo per 1 come è possibile ispezionare in strumenti React Developer.

Ciò dimostra che reagiscono

  • code chiama tutte le setState

  • torna a questa coda dopo l'esecuzione l'ultimo metodo nel contesto (la divCountHandler in questo caso)

  • unisce tutte le mutazioni di oggetti che si verificano all'interno di più chiamate setState nello stesso contesto (tutte le chiamate di metodo all'interno di una singola fase di evento è lo stesso contesto per es.) in una sintassi di mutazione di un singolo oggetto (la fusione rende sens E perché questo è il motivo per cui possiamo aggiornare le proprietà di stato in modo indipendente in setState() in primo luogo)

  • e lo passa in un singolo setState() per impedire il re-rendering a causa di più chiamate setState() (questo è una descrizione molto primitiva del dosaggio).

codice risultante gestito da reagire:

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

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

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

Dopo l'ultimo metodo termina l'esecuzione e quando ritorna reagire per elaborare la coda setState, chiama semplicemente il callback per ogni setState coda, passando nel precedente stato del componente.

In questo modo reagisce assicurando che l'ultimo richiamo in coda arrivi ad aggiornare lo stato su cui tutte le sue precedenti controparti hanno posto le mani.

Problemi correlati