Reputation: 3343
Here's my situation:
A subpar solution:
Please see code snippet below:
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>
);
}
});
Upvotes: 196
Views: 262330
Reputation: 15587
For completeness, thought I'd mention the hooks version of the setState callback function:
// problem: each batched state update overrides previous values
function handleClickNope() {
setAge(age + 1); // setAge(42 + 1)
setAge(age + 1); // setAge(42 + 1)
setAge(age + 1); // setAge(42 + 1)
}
// solution: 'updater function' always operates on current state
function handleClickYup() {
setAge(prevAge => prevAge + 1); // setAge(42 => 43)
setAge(prevAge => prevAge + 1); // setAge(43 => 44)
setAge(prevAge => prevAge + 1); // setAge(44 => 45)
}
https://react.dev/reference/react/useState#updating-state-based-on-the-previous-state
Upvotes: 0
Reputation: 1842
If someone here landed and having the same situation using hooks, the same behavior can be achived via the below process
const [data, setData] = useState(false);
useEffect(() => {
doSomething(); // This will be executed when the state changes
}, [data]);
setData(true);
Here useEffect
will run after any change in data, and we can execute any dependent task.
Upvotes: 61
Reputation: 1280
this.setState({
originId: input.originId,
destinationId: input.destinationId,
radius: input.radius,
search: input.search
},
function() {
console.log("setState completed", this.state)
}
)
Upvotes: 22
Reputation: 522
setState
takes new state and optional callback function which is called after the state has been updated.
this.setState(
{newState: 'whatever'},
() => {/*do something after the state has been updated*/}
)
Upvotes: 12
Reputation: 20851
Why not one more answer? setState()
and the setState()
-triggered render()
have both completed executing when you call componentDidMount()
(the first time render()
is executed) and/or componentDidUpdate()
(any time after render()
is executed). (Links are to ReactJS.org docs.)
Example with componentDidUpdate()
Caller, set reference and set state...
<Cmp ref={(inst) => {this.parent=inst}}>;
this.parent.setState({'data':'hello!'});
Render parent...
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!"
}
Example with componentDidMount()
Caller, set reference and set state...
<Cmp ref={(inst) => {this.parent=inst}}>
this.parent.setState({'data':'hello!'});
Render parent...
render() { // render() gets called anytime setState() is called
return (
<ChildComponent
state={this.state}
/>
);
}
After parent rerenders child, see state in componentDidUpdate()
.
componentDidMount() { // componentDidMount() gets called anytime setState()/render() finish
console.log(this.props.state.data); // output: "hello!"
}
Upvotes: 0
Reputation: 1624
According to the docs of setState()
the new state might not get reflected in the callback function findRoutes()
. Here is the extract from React docs:
setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value.
There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.
So here is what I propose you should do. You should pass the new states input
in the callback function 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
}
The findRoutes()
function should be defined like this:
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
}
Upvotes: 11
Reputation: 14101
setState()
has an optional callback parameter that you can use for this. You only need to change your code slightly, to this:
// 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
);
Notice the call to findRoutes
is now inside the setState()
call,
as the second parameter.
Without ()
because you are passing the function.
Upvotes: 328