React.js, poczekaj, aż setState zakończy działanie przed uruchomieniem funkcji?

108

Oto moja sytuacja:

  • on this.handleFormSubmit () Wykonuję this.setState ()
  • wewnątrz this.handleFormSubmit (), wzywam this.findRoutes (); - co zależy od pomyślnego zakończenia this.setState ()
  • this.setState (); nie kończy się przed tym. findRoutes zostanie wywołany ...
  • Jak mam czekać, aż this.setState () wewnątrz this.handleFormSubmit () zakończy się przed wywołaniem this.findRoutes ()?

Słabe rozwiązanie:

  • umieszczenie this.findRoutes () w componentDidUpdate ()
  • jest to niedopuszczalne, ponieważ będzie więcej zmian stanu niezwiązanych z funkcją findRoutes (). Nie chcę wyzwalać funkcji findRoutes () po zaktualizowaniu niepowiązanego stanu.

Zobacz fragment kodu poniżej:

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
źródło

Odpowiedzi:

251

setState()ma opcjonalny parametr wywołania zwrotnego, którego możesz użyć do tego. Musisz tylko nieznacznie zmienić kod, na ten:

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

Zwróć uwagę, że wywołanie findRoutesjest teraz wewnątrz setState()wywołania, jako drugi parametr.
Bez, ()ponieważ przechodzisz funkcję.

wintvelt
źródło
2
Niesamowity!
Wielkie
Będzie to dobrze działać przy resetowaniu AnimatedValue po ustawieniu setState w ReactNative.
SacWebDeveloper
Świetnie ~ Wielkie dzięki
吳 強 福
2
Ogólna wersjathis.setState({ name: "myname" }, function() { console.log("setState completed", this.state) })
Sasi Varunan
Nie wygląda na to, że możesz przekazać więcej niż jedno wywołanie zwrotne do metody setState. Czy istnieje sposób łączenia callbacków w prosty sposób? Powiedzmy, że mam 3 metody, które wszystkie muszą działać, i wszystkie stan aktualizacji. Jaki jest preferowany sposób radzenia sobie z tym?
Sean
17
       this.setState(
        {
            originId: input.originId,
            destinationId: input.destinationId,
            radius: input.radius,
            search: input.search
        },
        function() { console.log("setState completed", this.state) }
       )

to może być pomocne

Harshit Singhai
źródło
10

Zgodnie z dokumentacją setState()nowy stan może nie zostać odzwierciedlony w funkcji wywołania zwrotnego findRoutes(). Oto wyciąg z dokumentacji React :

setState () nie zmienia natychmiastowo this.state, ale tworzy oczekującą zmianę stanu. Dostęp do this.state po wywołaniu tej metody może potencjalnie zwrócić istniejącą wartość.

Nie ma gwarancji synchronicznego działania wywołań setState, a wywołania mogą być grupowane w celu zwiększenia wydajności.

Oto, co proponuję, abyś zrobił. Nowe stany należy przekazać inputw funkcji wywołania zwrotnego 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
}

findRoutes()Funkcja powinna być zdefiniowana następująco:

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
źródło
ma poważną wadę - przekazanie dosłownego przedmiotu do, setState()ponieważ nowy stan nie jest dobry, ponieważ prowadzi do warunków wyścigu
tar.
oto kolejny cytat z react docs (który mógł zostać zaktualizowany, odkąd opublikowałeś swoją odpowiedź): "... użyj funkcji componentDidUpdate lub wywołania zwrotnego setState (setState (updater, callback)), z których każdy zostanie uruchomiony po zakończeniu aktualizacji zastosowano ”. To mówi mi, że nowy stan z całą pewnością znajduje odzwierciedlenie w funkcji wywołania zwrotnego.
Andy
1

setState przyjmuje nowy stan i opcjonalną funkcję zwrotną, która jest wywoływana po zaktualizowaniu stanu.

this.setState(
  {newState: 'whatever'},
  () => {/*do something after the state has been updated*/}
)
hackhan
źródło
0

Dlaczego nie jeszcze jedna odpowiedź? setState()i setState()-triggered render()zakończyły wykonywanie podczas wywołania componentDidMount()(pierwsze render()wykonanie) i / lub componentDidUpdate()(w dowolnym momencie po render()wykonaniu). (Linki prowadzą do dokumentacji ReactJS.org).

Przykład z componentDidUpdate()

Dzwoniący, ustaw referencję i ustaw stan ...

<Cmp ref={(inst) => {this.parent=inst}}>;
this.parent.setState({'data':'hello!'});

Renderuj rodzic ...

componentDidMount() {           // componentDidMount() gets called after first state set
    console.log(this.state.data);   // output: "hello!"
}
componentDidUpdate() {          // componentDidUpdate() gets called after all other states set
    console.log(this.state.data);   // output: "hello!"
}

Przykład z componentDidMount()

Dzwoniący, ustaw referencję i ustaw stan ...

<Cmp ref={(inst) => {this.parent=inst}}>
this.parent.setState({'data':'hello!'});

Renderuj rodzic ...

render() {              // render() gets called anytime setState() is called
    return (
        <ChildComponent
            state={this.state}
        />
    );
}

Gdy rodzic przekaże dziecko, zobacz stan w componentDidUpdate().

componentDidMount() {           // componentDidMount() gets called anytime setState()/render() finish
console.log(this.props.state.data); // output: "hello!"
}
HoldOffHunger
źródło