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!
This solved it :)
getDistanceMatrix = (address, store) => {
return new Promise((resolve) => {
let service = new google.maps.DistanceMatrixService();
let origin = [address];
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)
const newStores = JSON.parse(JSON.stringify(this.state.stores))
Promise.all(newStores.map(store => this.getDistanceMatrix(address, store))).then(newStore => {
newStores.sort((a,b) => a.distance - b.distance);
this.setState({stores : newStores})
})
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.