React.js, attendere che setState finisca prima di attivare una funzione?


106

Ecco la mia situazione:

  • su this.handleFormSubmit () sto eseguendo this.setState ()
  • dentro this.handleFormSubmit (), chiamo this.findRoutes (); - che dipende dal completamento con successo di this.setState ()
  • this.setState (); non viene completato prima di questo .indRoutes viene chiamato ...
  • Come aspetto che this.setState () all'interno di this.handleFormSubmit () finisca prima di chiamare this.findRoutes ()?

Una soluzione scadente:

  • mettendo this.findRoutes () in componentDidUpdate ()
  • questo non è accettabile perché ci saranno più cambiamenti di stato non correlati alla funzione findRoutes (). Non voglio attivare la funzione findRoutes () quando lo stato non correlato viene aggiornato.

Vedere lo snippet di codice di seguito:

handleFormSubmit: function(input){
                // Form Input
                this.setState({
                    originId: input.originId,
                    destinationId: input.destinationId,
                    radius: input.radius,
                    search: input.search
                })
                this.findRoutes();
            },
            handleMapRender: function(map){
                // Intialized Google Map
                directionsDisplay = new google.maps.DirectionsRenderer();
                directionsService = new google.maps.DirectionsService();
                this.setState({map: map});
                placesService = new google.maps.places.PlacesService(map);
                directionsDisplay.setMap(map);
            },
            findRoutes: function(){
                var me = this;
                if (!this.state.originId || !this.state.destinationId) {
                    alert("findRoutes!");
                    return;
                }
                var p1 = new Promise(function(resolve, reject) {
                    directionsService.route({
                        origin: {'placeId': me.state.originId},
                        destination: {'placeId': me.state.destinationId},
                        travelMode: me.state.travelMode
                    }, function(response, status){
                        if (status === google.maps.DirectionsStatus.OK) {
                            // me.response = response;
                            directionsDisplay.setDirections(response);
                            resolve(response);
                        } else {
                            window.alert('Directions config failed due to ' + status);
                        }
                    });
                });
                return p1
            },
            render: function() {
                return (
                    <div className="MapControl">
                        <h1>Search</h1>
                        <MapForm
                            onFormSubmit={this.handleFormSubmit}
                            map={this.state.map}/>
                        <GMap
                            setMapState={this.handleMapRender}
                            originId= {this.state.originId}
                            destinationId= {this.state.destinationId}
                            radius= {this.state.radius}
                            search= {this.state.search}/>
                    </div>
                );
            }
        });

Risposte:


245

setState()ha un parametro di callback opzionale che puoi usare per questo. Hai solo bisogno di cambiare leggermente il tuo codice, in questo:

// Form Input
this.setState(
  {
    originId: input.originId,
    destinationId: input.destinationId,
    radius: input.radius,
    search: input.search
  },
  this.findRoutes         // here is where you put the callback
);

Notare che la chiamata a findRoutesè ora all'interno della setState()chiamata, come secondo parametro.
Senza ()perché stai passando la funzione.


2
Sorprendente! Grazie mille
malexanders

Funzionerà bene per reimpostare un AnimatedValue dopo setState in ReactNative.
SacWebDeveloper

Fantastico ~ Grazie mille
吳 強 福

2
Una versione genericathis.setState({ name: "myname" }, function() { console.log("setState completed", this.state) })
Sasi Varunan

Non sembra che tu possa passare più di una richiamata a setState. Esiste un modo non disordinato per concatenare i callback? Diciamo che ho 3 metodi che devono essere tutti eseguiti e tutto lo stato di aggiornamento. Qual è il modo preferito per gestirlo?
Sean

17
       this.setState(
        {
            originId: input.originId,
            destinationId: input.destinationId,
            radius: input.radius,
            search: input.search
        },
        function() { console.log("setState completed", this.state) }
       )

questo potrebbe essere utile


10

Secondo i documenti del setState()nuovo stato potrebbe non riflettersi nella funzione di callback findRoutes(). Ecco l'estratto dai documenti di React :

setState () non modifica 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 vi è alcuna garanzia di funzionamento sincrono delle chiamate a setState e le chiamate possono essere raggruppate in batch per migliorare le prestazioni.

Quindi ecco cosa ti propongo di fare. È necessario passare i nuovi stati inputnella funzione di callback findRoutes().

handleFormSubmit: function(input){
    // Form Input
    this.setState({
        originId: input.originId,
        destinationId: input.destinationId,
        radius: input.radius,
        search: input.search
    });
    this.findRoutes(input);    // Pass the input here
}

La findRoutes()funzione dovrebbe essere definita in questo modo:

findRoutes: function(me = this.state) {    // This will accept the input if passed otherwise use this.state
    if (!me.originId || !me.destinationId) {
        alert("findRoutes!");
        return;
    }
    var p1 = new Promise(function(resolve, reject) {
        directionsService.route({
            origin: {'placeId': me.originId},
            destination: {'placeId': me.destinationId},
            travelMode: me.travelMode
        }, function(response, status){
            if (status === google.maps.DirectionsStatus.OK) {
                // me.response = response;
                directionsDisplay.setDirections(response);
                resolve(response);
            } else {
                window.alert('Directions config failed due to ' + status);
            }
        });
    });
    return p1
}

questo ha un grave difetto - passare un obj letterale a setState()come il nuovo stato non è buono perché porta a condizioni di gara
tar

ecco un'altra citazione dai documenti di reazione (che potrebbero essere stati aggiornati da quando hai pubblicato la tua risposta): "... usa componentDidUpdate o un callback setState (setState (updater, callback)), entrambi sono garantiti per attivarsi dopo che l'aggiornamento è terminato stato applicato ". Questo mi dice che il nuovo stato si riflette sicuramente nella funzione di callback.
Andy
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.