React.js, esperar setState terminar antes de acionar uma função?

106

Esta é minha situação:

  • on this.handleFormSubmit () Estou executando this.setState ()
  • dentro de this.handleFormSubmit (), estou chamando this.findRoutes (); - que depende da conclusão bem-sucedida de this.setState ()
  • this.setState (); não é concluído antes que this.findRoutes seja chamado ...
  • Como espero que this.setState () dentro de this.handleFormSubmit () termine antes de chamar this.findRoutes ()?

Uma solução abaixo da média:

  • colocando this.findRoutes () em componentDidUpdate ()
  • isso não é aceitável porque haverá mais mudanças de estado não relacionadas à função findRoutes (). Não quero acionar a função findRoutes () quando o estado não relacionado é atualizado.

Por favor, veja o snippet de código abaixo:

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>
                );
            }
        });
malexanders
fonte

Respostas:

245

setState()tem um parâmetro de retorno de chamada opcional que você pode usar para isso. Você só precisa alterar seu código ligeiramente, para este:

// 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 a chamada para findRoutesagora está dentro da setState()chamada, como o segundo parâmetro.
Sem ()porque você está passando a função.

Wintvelt
fonte
2
Surpreendente! Muito obrigado
malexanders
Isso funcionará bem para redefinir um AnimatedValue após setState em ReactNative.
SacWebDeveloper
Ótimo ~ Muito obrigado
吳 強 福
2
Uma versão genéricathis.setState({ name: "myname" }, function() { console.log("setState completed", this.state) })
Sasi Varunan
Não parece que você pode passar mais de um retorno de chamada para setState. Existe uma maneira não complicada de encadear retornos de chamada? Vamos dizer que tenho 3 métodos que precisam ser executados e todos atualizam o estado. Qual é a forma preferida de lidar com isso?
Sean
17
       this.setState(
        {
            originId: input.originId,
            destinationId: input.destinationId,
            radius: input.radius,
            search: input.search
        },
        function() { console.log("setState completed", this.state) }
       )

isso pode ser útil

Harshit Singhai
fonte
10

De acordo com os documentos do setState()novo estado, pode não ser refletido na função de retorno de chamada findRoutes(). Aqui está o extrato dos documentos do React :

setState () não altera imediatamente this.state, mas cria uma transição de estado pendente. Acessar this.state após chamar esse método pode potencialmente retornar o valor existente.

Não há garantia de operação síncrona de chamadas para setState e as chamadas podem ser agrupadas para ganhos de desempenho.

Portanto, aqui está o que proponho que você faça. Você deve passar os novos estados inputna função de retorno de chamada 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
}

A findRoutes()função deve ser definida assim:

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
fonte
isso tem uma falha séria - passar uma objeção literal setState()como o novo estado não é bom porque leva a condições de corrida
tar
aqui está outra citação dos documentos react (que podem ter sido atualizados desde que você postou sua resposta): "... use componentDidUpdate ou um retorno de chamada setState (setState (atualizador, retorno de chamada)), ambos com garantia de disparar após a atualização foi aplicado ". Isso me diz que o novo estado definitivamente se reflete na função de retorno de chamada.
Andy