React.js, ¿esperar a que setState termine antes de activar una función?

106

Esta es mi situación:

  • en this.handleFormSubmit () Estoy ejecutando this.setState ()
  • dentro de this.handleFormSubmit (), estoy llamando a this.findRoutes (); - que depende de la finalización exitosa de this.setState ()
  • this.setState (); no se completa antes de que se llame a this.findRoutes ...
  • ¿Cómo espero a que termine this.setState () dentro de this.handleFormSubmit () antes de llamar a this.findRoutes ()?

Una solución insatisfactoria:

  • poniendo this.findRoutes () en componentDidUpdate ()
  • esto no es aceptable porque habrá más cambios de estado no relacionados con la función findRoutes (). No quiero activar la función findRoutes () cuando se actualiza el estado no relacionado.

Consulte el fragmento de código a continuación:

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>
                );
            }
        });
malexander
fuente

Respuestas:

245

setState()tiene un parámetro de devolución de llamada opcional que puede usar para esto. Solo necesita cambiar su código ligeramente, a esto:

// 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
);

Observe que la llamada a findRoutesahora está dentro de la setState()llamada, como segundo parámetro.
Sin ()porque estás pasando la función.

Wintvelt
fuente
2
¡Asombroso! Muchas gracias
malexanders
Esto funcionará muy bien para restablecer un AnimatedValue después de setState en ReactNative.
SacWebDeveloper
Genial ~ Muchas gracias
吳 強 福
2
Una versión genéricathis.setState({ name: "myname" }, function() { console.log("setState completed", this.state) })
Sasi Varunan
No parece que pueda pasar más de una devolución de llamada a setState. ¿Existe una forma sencilla de encadenar devoluciones de llamada? Digamos que tengo 3 métodos que todos deben ejecutarse y todos están actualizados. ¿Cuál es la forma preferida de manejar esto?
Sean
17
       this.setState(
        {
            originId: input.originId,
            destinationId: input.destinationId,
            radius: input.radius,
            search: input.search
        },
        function() { console.log("setState completed", this.state) }
       )

esto podría ser útil

Harshit Singhai
fuente
10

Según los documentos del setState()nuevo estado, es posible que no se refleje en la función de devolución de llamada findRoutes(). Aquí está el extracto de los documentos de React :

setState () no muta inmediatamente this.state pero crea una transición de estado pendiente. Acceder a this.state después de llamar a este método puede devolver potencialmente el valor existente.

No hay garantía de funcionamiento sincrónico de llamadas a setState y las llamadas se pueden agrupar para mejorar el rendimiento.

Así que esto es lo que te propongo que hagas. Debe pasar los nuevos estados inputen la función de devolución de llamada 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()función debe definirse así:

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
}
Pawan Samdani
fuente
esto tiene un grave defecto - pasar un obj literal setState()como el nuevo estado no es bueno, ya que conduce a condiciones de carrera
tar
aquí hay otra cita de los documentos de reacción (que podrían haberse actualizado desde que publicó su respuesta): "... use componentDidUpdate o una devolución de llamada setState (setState (actualizador, devolución de llamada)), cualquiera de los cuales está garantizado para activarse después de que se haya actualizado la actualización se ha aplicado ". Esto me dice que el nuevo estado definitivamente se refleja en la función de devolución de llamada.
Andy