The function I've made which purpose is to update state does not work perfectly.
I make a deep copy of my current state which I modify, then I set the state to that changed copy. I decided to run a simple map on the copy and update a property on each object in the array, this made the app update state immediately and show the changes. (not shown in the code below, something I just tried)
But it does not work with the modifications I truly want to make to the copy. Since the setState happens async, I understand that the changes don't happen immediately, how could I solve this? Because the changes are seen when the function is triggered a second time, but I want it to happen on the first run. You could see a live demo of what I'm building at https://non.mpedersen.me/ if you want.
Function in App.js
addressSearch(address){
console.log('firing search for distance. Origin is: ' + address)
let origin = [address];
const newStores = JSON.parse(JSON.stringify(this.state.stores))
let service = new google.maps.DistanceMatrixService();
newStores.map(store => service.getDistanceMatrix({
origins: origin,
destinations: store.addressapi,
travelMode: 'DRIVING',
}, result => {
store.distance = result.rows["0"].elements["0"].distance.text.replace(/\Dkm/, '').replace(/,/,'.').replace(/\s/, '');
store.duration = result.rows["0"].elements["0"].duration.text;
}))
newStores.sort((a,b) => a.distance - b.distance);
this.setState({stores : newStores})
}
You must setState after all getDistanceMatrix are finished and your map is done. Because that function runs async, you are not really getting the function completion when you setState. The completion happens in another stack.
Promises are really good because they help dealing with scenarios like this. This is a really nice question because it shows the nature of JS mechanisms (event-loop).
Try this code but know that I haven't tested/runned it, if you get the idea it will get you to the right place:
const getDistanceMatrix = (store) => {
return new Promise((resolve) => {
service.getDistanceMatrix({
origins: origin,
destinations: store.addressapi,
travelMode: 'DRIVING',
}, result => {
store.distance = result.rows["0"].elements["0"].distance.text.replace(/\Dkm/, '').replace(/,/,'.').replace(/\s/, '');
store.duration = result.rows["0"].elements["0"].duration.text;
resolve(store)
})
})
}
addressSearch(address){
console.log('firing search for distance. Origin is: ' + address)
let origin = [address];
const newStores = JSON.parse(JSON.stringify(this.state.stores))
let service = new google.maps.DistanceMatrixService();
Promise.all(newStores.map(getDistanceMatrix).then((newStores) => {
newStores.sort((a,b) => a.distance - b.distance);
this.setState({stores : newStores})
})
}
BTW, setting state asynchronously after a Promise is resolved may lead to errors if the host component is not mounted anymore. That's actually an excellent reason to adopt a pattern like redux, it simplifies things like this a lot!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With