ReactJS -> store variable from promise

Viewed 2314

I'm trying to figure out how to store promise return stages into variables. In this use case, I'm using google maps and google's geocoder to transform a JSON file of addresses into an array of lat lng objects in order to make markers, but I'm curious regardless. It's been done through a single promise, but as I'm forced to loop through the data and execute the call, I can't call another function and leave the promise, as I have to repeat the promise eventually. This means I need to store the object in a separate variable. See the sections of code below.

address.json

{ 
  "address" : "Oosterpark 9",
  "city" : "Amsterdam",
  "name" : "Onze Lieve Vrouwe Gasthuis"
  "zipcode" : "1091 AC"
},
{
  "address" : "Jan Tooropstraat 164",
  "city" : "Amsterdam",
  "name" : "Sint Lucas Andreas Hospital"
  "zipcode" : "1061 AE"
}

Map.js

makeMarker(a){
    Geocode.setApiKey(this.apiKey);
    let arr = [];
    for(let i=0; i < a.length; i++){
        let address = b[i].address.replace(" ","+") + ',+'+b[i].zipcode.replace(" ","+")+',+'+b[i].city;

        arr[i] = Geocode.fromAddress(address).then(
            response => {
                const {lat, lng} = response.results[0].geometry.location;
                console.log(lat, lng);
                return {lat: lat, lng: lng};
            }
        );
    }
return arr;
}

In this case the above function is called on in the constructor where a variable is waiting to be defined by the outcome of the makeMarker function. In this case, it only returns [promise, promise], when it's supposed to return [{lat: __, lng: __,}, {lat: __, lng: __,}].

I hope to hear some of your ideas on how to solve this use case!

2 Answers

You can use Promise.all, create an array of promises on the instance of the class, then on componentDidMount (or other useful life cycle hook) run:

Promise.all(promies).then(...)

So something like this:

makeMarker(a){
    Geocode.setApiKey(this.apiKey);
    let arr = [];
    for(let i=0; i < a.length; i++){
        let address = b[i].address.replace(" ","+") + ',+'+b[i].zipcode.replace(" ","+")+',+'+b[i].city;

        arr[i] = Geocode.fromAddress(address)
    }
    this.promises = arr;
}

Then elsewhere in your code:

Promise.all(this.promises).then(
  listOfResponses => {
     // rest of your code
  }
)

Here is a small example of such usage with Promise.all

const data = [1, 2, 3, 4, 5, 6];

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      fetchedData: []
    }
    this.promises = data.map(d => {
      return new Promise((resolve) => {
        setTimeout(() => resolve(d),1500);
      })
    });
  }

  componentDidMount() {
    Promise.all(this.promises).then(fetchedData => this.setState({ fetchedData }));
  }
  
  render() {
    const { fetchedData } = this.state;
    return (
      <div>
        {fetchedData.length ? fetchedData.map(d => <div>{d}</div>) : "Loading..."}
      </div>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

@Sagiv b.g' s answer of using Promise.all is the best way. Here is another approach.

var arr = [];
makeMarker(a){
    Geocode.setApiKey(this.apiKey);
   
    for(let i=0; i < a.length; i++){
        let address = b[i].address.replace(" ","+") + ',+'+b[i].zipcode.replace(" ","+")+',+'+b[i].city;

        Geocode.fromAddress(address).then(
            response => {
                const {lat, lng} = response.results[0].geometry.location;
                console.log(lat, lng);
                 addToArr({lat: lat, lng: lng},b);
            }
        );
    }

}

addToArr(latLongObj,b){
  arr.push(latLongObj);
  if(arr.length >= b.length){
    //callYourfunction and do the logic
  }
}

Related