Search code examples
reactjspromisemapsmarkergeocode

React get object-values outside of .then // create map markers out of geocodes


I commented my code to make clear what should happen. I tried for hours to create the markers from the geocodes, however I can't seem to figure out how to extract the values from the promise. I fiddled with useState and useEffect, but neither worked.

const GMap = eventList => {
  return (
    <GoogleMap defaultZoom={10} defaultCenter={{ lat: 51.62919, lng: 7.3928 }}>
      {
          // Map through location-strings in eventList
          eventList.eventList.map(event => {
              // Get all geocodes
        Geocode.fromAddress(event.city).then(
          response => {
            const { lat, lng } = response.results[0].geometry.location;
            console.log(lat, lng);
          },
          error => {
            console.error(error);
          }
        );
        return (
            // Create marker for every geocode (lat/lng pair)
          <Marker
            key={event._id}
            position={{
              lat: 51.62919, // How to insert the lat and lng values from response instead?
              lng: 7.3928
            }}
          />
        );
      })}
    </GoogleMap>
  );
};

Thanks for helping out, it gets really frustrating by now to not solve this even tho it seems trivial at first.


Solution

  • const GMap = ({ eventList }) => {
      const [markers, setMarkers] = React.useState([]);
    
      React.useEffect(() => {
        eventList.forEach(async event => {
          const response = await Geocode.fromAddress(event.city);
          const { lat, lng } = response.results[0].geometry.location;
    
          const marker = (
            <Marker
              key={event._id}
              position={{
                lat,
                lng
              }}
            />
          );
    
          setMarkers(prevMarkers => [...prevMarkers, marker]);
        });
      }, [eventList]);
    
      return (
        <GoogleMap defaultZoom={10} defaultCenter={{ lat: 51.62919, lng: 7.3928 }}>
          {markers}
        </GoogleMap>
      );
    };
    
    eventList.forEach(async event => {
          const response = await Geocode.fromAddress(event.city);
    

    We're going to loop over eventList and process the result as each time Geocode.fromAddress returns, rather than doing it in a map. The issue with a map is you are limiting yourself for the whole map to be processed first before displaying anything. This method allows you to render markers as they are returned.

    setMarkers(prevMarkers => [...prevMarkers, marker]);
    

    Using useState's callback, we use the previous markers and create a new array, appending the previous marker to it, so it slowly builds up as each Geocode.fromAddress(event.city); is returned.

    A working CodeSandbox is here https://codesandbox.io/s/wizardly-paper-3eu03 I had to stub out GoogleMap, Markers and Geocode.

    Geocode, to simulate the real-world, has a random setTimeout so you can see each result being loaded as they are ready.