javascriptreactjsgoogle-mapsgoogle-maps-react

Multiple markers on google map React with a .map


I am currently trying to loop through an array and display multiple markers on a google map in my react app. I am first having to run the locations through a geocoding api to get the lat and lng and then setting a state variable to map through to get the markers to display onto the map. I am currently having issues somewhere in my code and cant seem to find the problem.

I know that when I console.log my state variable, it seems to run the log several times, with the first several being empty arrays and the last several containing the data that I need. Im very new to react and still trying to get a grasp on it.

Also I think that my .map function is running before the state variable is set and I'm not sure how to refactor to account for this.

here is my code -

import React, { useContext, useEffect, useState } from 'react';
import { GoogleMap, useLoadScript, Marker } from "@react-google-maps/api"
import Settings from "../Settings"
import mapSyles from "./MapStyles"
import { LogContext } from '../divelog/DiveLogProvider';

export const MapRender =(props) => {
    const {diveLogs} = useContext(LogContext)
    const [latLong, setLatLong] = useState([])
    

    
     useEffect(()=>{
    //Taking the logs and running them through API to get lat and lng for each location 
    let latLongs = []
    diveLogs.map(dl =>{
        return fetch(`http://api.positionstack.com/v1/forward?access_key=ff0fcd042ab984146219abc275c71e4b&query=${dl.location}&limit=1
        `)
            .then(res => res.json())
            .then(parsedRes => {

                latLongs.push(parsedRes.data[0])
                setLatLong(latLongs)
              })


    })
 
},[diveLogs])


// this returns several logs, the first of which are empty arrays and the last are correct with the data that I need
    console.log(latLong)

    


    const { isLoaded, loadError } = useLoadScript({
        googleMapsApiKey: Settings.apiKey
    })

    const mapContainerStyle = {
        width: '31rem',
        height: '24rem'
    }

    const center = {
        lat: 0,
        lng: 0
    }

    const options = {
        styles: mapSyles,
        disableDefaultUI: true
    }


    
    if (loadError) console.log("error loading maps")
    if (!isLoaded) return "Loading..."

    return (
        <div>
            <GoogleMap
                mapContainerStyle={mapContainerStyle}
                options={options}
                zoom={1}
                center={center}
            >
                
                {
                    //this is where I map through the state variable
                    latLong.map(l =>(
                     <Marker key={l.lat}
                         position ={{lat: l.latitude, lng: l.longitude}} 
                         />
                    ))
                }
            </GoogleMap>
        </div>
    )
}

Solution

  • you need to move your setLatLong(myDiveLogs) inside the success method of api call. Api call is async but assigning data in setLatLong is sync. React will push empty data in setLatLong.

    You need to wait for api call and once data is available set the value in it.

    import React, {
      useContext,
      useEffect,
      useState
    } from 'react';
    import {
      GoogleMap,
      useLoadScript,
      Marker
    } from "@react-google-maps/api"
    import Settings from "../Settings"
    import mapSyles from "./MapStyles"
    import {
      LogContext
    } from '../divelog/DiveLogProvider';
    
    export const MapRender = (props) => {
      const {
        diveLogs
      } = useContext(LogContext)
      const [latLong, setLatLong] = useState([])
    
    
    
      useEffect(() => {
        //Taking the logs and running them through API to get lat and lng for each location 
        let myDiveLogs = []
        diveLogs.map(dl => {
          return fetch(`http://api.positionstack.com/v1/forward?access_key=MYKEY&query=${dl.location}&limit=1
                `)
            .then(res => res.json())
            .then(parsedRes => {
    
              myDiveLogs.push(parsedRes.data[0])
              setLatLong(myDiveLogs)
            })
    
    
        })
    
      }, [diveLogs])
    
      // this returns several logs, the first of which are empty arrays and the last are correct with the data that I need
      console.log(latLong)
    
    
    
    
      const {
        isLoaded,
        loadError
      } = useLoadScript({
        googleMapsApiKey: Settings.apiKey
      })
    
      const mapContainerStyle = {
        width: '31rem',
        height: '24rem'
      }
    
      const center = {
        lat: 0,
        lng: 0
      }
    
      const options = {
        styles: mapSyles,
        disableDefaultUI: true
      }
    
    
    
      if (loadError) console.log("error loading maps")
      if (!isLoaded) return "Loading..."
    
      return ( <
        div >
        <
        GoogleMap mapContainerStyle = {
          mapContainerStyle
        }
        options = {
          options
        }
        zoom = {
          1
        }
        center = {
          center
        } >
    
        {
          //this is where I map through the state variable
          latLong.map(l => ( <
            Marker key = {
              l.lat
            }
            position = {
              {
                lat: l.latitude,
                lng: l.longitude
              }
            }
            />
          ))
        } <
        /GoogleMap> <
        /div>
      )
    }