javascript - Cum pot aștepta ca setState să se termine înainte de a declanșa o funcție în React?


Iată situația mea:

  • pe this.handleFormSubmit() eu execut this.setState()
  • în interiorul acestui.handleFormSubmit(), apelez acest.findRoutes(); - care depinde de finalizarea cu succes a acestui.setState()
  • this.setState(); nu se finalizează înainte ca this.findRoutes să fie apelat...
  • Cum pot să aștept ca this.setState() din cadrul this.handleFormSubmit() să se termine înainte de a apela this.findRoutes()?

O soluție substandard:

  • punând acest.this.findRoutes() în componentDidUpdate()
  • acest lucru nu este acceptabil deoarece vor exista mai multe schimbări de stare care nu au legătură cu funcția findRoutes(). Nu vreau să declanșez funcția findRoutes() atunci când se actualizează o stare nelegată.

Vă rugăm să vedeți fragmentul de cod de mai jos:

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

Răspuns #1

setState() are un parametru opțional de tip callback pe care îl poți folosi pentru asta. Trebuie doar să îți modifici puțin codul, astfel:

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

Observați că apelul către findRoutes este acum în interiorul apelului setState(), ca al doilea parametru.
Fără () deoarece transmiteți funcția.

Răspunsul #2

Dacă cineva aici a ajuns și se află în aceeași situație folosind hooks, același comportament poate fi obținut prin procesul de mai jos

const [data, setData] = useState(false);

useEffect(() => {
    doSomething(); // This will be executed when the state changes
}, [data]);

setData(true);

Aici useEffect va rula după orice modificare a datelor și putem executa orice sarcină dependentă.

Răspunsul #3

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

Răspunsul #4

setState primește o stare nouă și o funcție de apelare opțională, care este apelată după ce starea a fost actualizată.

this.setState(
  {newState: 'whatever'},
  () => {/*do something after the state has been updated*/}
)

Răspunsul #5

Conform documentației pentru setState(), noua stare s-ar putea să nu fie reflectată în funcția callback findRoutes(). Iată un extras din documentația React:

setState() nu modifică imediat this.state, ci creează o tranziție de stare în așteptare. Accesarea this.state după apelarea acestei metode poate returna valoarea existentă.

Nu există nicio garanție a funcționării sincrone a apelurilor către setState și apelurile pot fi grupate pentru câștiguri de performanță.

Așadar, iată ce vă propun să faceți. Ar trebui să transmiteți noile stări input în funcția de apel 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
}

Funcția findRoutes() ar trebui definită astfel:

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
}