简体   繁体   中英

How do I make my React state update immediately?

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM