0

I made a Google Maps map with a draggable marker in react. When the user drags the marker, I need to know the new latitude and longitude.

How can I retrieve the new coordinates?

I don't understand what is the best approach to doing that in react.

1 Answers1

0

Here is an example of how to do that with Typescript.

Basically, you need to have a state to track the latitude and longitude. On the MarkerF component in which you import from @react-google-maps/api, you can pass in a callback function to update the lat/long when you finish dragging the marker.

type MapOptions = google.maps.MapOptions
type SearchBox = google.maps.places.SearchBox
type Map = google.maps.Map
type MapMouseEvent = google.maps.MapMouseEvent
type Props = {
  lat: number
  lng: number
}

/**
 * Map component using Google Map API
 * The map is centered at the given lat and lng initially but location can also be updated by searching with the search box
 * @param {number} lat - latitude
 * @param {number} lng - longitude
 * @returns
 */
const Map: React.FC<Props> = ({ lat, lng }) => {
  // Ref to the google map instance
  const mapRef = useRef<Map>()
  // Ref to the search box instance
  const searchBoxRef = useRef<SearchBox>()
  // Options for the Google Map
  const mapOptions = useMemo<MapOptions>(() => ({}), [])
  // Location lat/lng state
  const [coordinates, setCoordinates] = React.useState({
    lat,
    lng,
  })

  // Handle event when the map is loaded
  const onLoad = useCallback((map: Map) => {
    mapRef.current = map
  }, [])

  // Handle event when the search box is loaded
  const onSearchBoxLoad = useCallback((searchBox: SearchBox) => {
    searchBoxRef.current = searchBox
  }, [])

  // Handle event when the location is changed
  const onPlacesChanged = () => {
    const searchBox = searchBoxRef.current

    if (!searchBox) return

    // Get the place results from the search box
    const places = searchBox.getPlaces()
    if (!places || places.length === 0) return

    // Get the lat and lng of the first result
    const lat = places[0].geometry?.location?.lat()
    const lng = places[0].geometry?.location?.lng()

    // Set the coordinates state
    if (lat && lng) {
      setCoordinates({ lat, lng })
    }
  }

  // Handle event when the marker is dragged to a new position
  const onDragEnd = (e: MapMouseEvent) => {
    // Get the lat and lng of the marker at new position
    const lat = e.latLng?.lat()
    const lng = e.latLng?.lng()

    // Update the coordinates state
    if (lat && lng) {
      setCoordinates({ lat, lng })
    }
  }

  return (
    <Box component="div" width="100vw" height="400px">
      <GoogleMap
        zoom={15}
        center={{
          lat: coordinates.lat,
          lng: coordinates.lng,
        }}
        mapContainerClassName="map-container"
        options={mapOptions}
        onLoad={onLoad}
      >
        {/* NOTE - need to use MarkerF for React v18 */}
        <MarkerF
          position={{
            lat: coordinates.lat,
            lng: coordinates.lng,
          }}
          draggable={true}
          onDragEnd={onDragEnd}
        />
        <StandaloneSearchBox
          onLoad={onSearchBoxLoad}
          onPlacesChanged={onPlacesChanged}
        >
          <input
            type="text"
            placeholder="Customized your placeholder"
            style={{
              boxSizing: `border-box`,
              border: `1px solid transparent`,
              width: `240px`,
              height: `32px`,
              padding: `0 12px`,
              borderRadius: `3px`,
              boxShadow: `0 2px 6px rgba(0, 0, 0, 0.3)`,
              fontSize: `14px`,
              outline: `none`,
              textOverflow: `ellipses`,
              position: 'absolute',
              left: '50%',
              marginLeft: '-120px',
            }}
          />
        </StandaloneSearchBox>
      </GoogleMap>
    </Box>
  )
}

Also, before doing this, in a parent component, you also need to have already loaded the Google Map with your API key. Here is an example

type Props = {
  latitude: number
  longitude: number
}
const SiteMap: React.FC<Props> = ({ latitude, longitude }) => {
  // The libraries to load
  const [libraries] = React.useState<
    ('places' | 'drawing' | 'geometry' | 'localContext' | 'visualization')[]
  >(['places'])

  // Load the Google Map API
  const { isLoaded } = useLoadScript({
    id: 'google-map-script',
    googleMapsApiKey: 'YOUR_API_KEY',
    libraries,
  })

  if (!isLoaded) return <div>Loading...</div>

  return <Map lat={latitude} lng={longitude} />
}
Huynh Triet
  • 81
  • 3
  • 3