38

I am using the google-map-react NPM package to create a Google Map and several markers.

Question: How can we add the default Google Maps markers to the map?

From this Github issue, it seems that we need to access the internal Google Maps API using the onGoogleApiLoaded function.

Referring to an example from the Google Maps JS API docs, we can use the following code to render the marker, but how do we define the references to map?

var marker = new google.maps.Marker({
    position: myLatLng,
    map: map,
    title: 'Hello World!'
});

Current Code:

renderMarkers() {
    ...
}

render() {
    return (
        <div style={{'width':800,'height':800}}>
            <GoogleMap
                bootstrapURLKeys={{key: settings.googleMapApiKey}}
                defaultZoom={13}
                defaultCenter={{ 
                    lat: this.props.user.profile.location.coordinates[1], 
                    lng: this.props.user.profile.location.coordinates[0]
                }}
                onGoogleApiLoaded={({map, maps}) => this.renderMarkers()}
                yesIWantToUseGoogleMapApiInternals
            >
            </GoogleMap>
        </div>
    );
}
Nyxynyx
  • 61,411
  • 155
  • 482
  • 830

4 Answers4

34

Edit:
Since this answer was posted the docs (and likely, the API) of the GoogleMapReact element was changed to support children. Any child with lat and lng would be rendered at the corresponding location on the map, as also indicated by @Jobsamuel's answer.

The onGoogleApiLoaded callback should not be used for this purpose, as it is inferior to the declarative style and would not be re-run if changes are made to the map.


Original answer (outdated):

This may not be entirely clear from the description in the Readme, but the maps argument is, in fact, the maps API object (and map is, of course, the current Google Map instance). Therefore, you should pass both to your method:

onGoogleApiLoaded={({map, maps}) => this.renderMarkers(map, maps)}

and use them:

renderMarkers(map, maps) {
  let marker = new maps.Marker({
    position: myLatLng,
    map,
    title: 'Hello World!'
  });
}
MasterAM
  • 16,283
  • 6
  • 45
  • 66
  • do you also know how to add the `infowindow`? I am having difficulties implementing this – Paul Fitzgerald Mar 16 '18 at 16:56
  • 1
    I have really used the raw Maps API with this package, but rather the declarative React API. Doesn't the [example](https://developers.google.com/maps/documentation/javascript/infowindows) on Google's API documentation work for you? If not, you should probably create a new question with your code and explanations. – MasterAM Mar 16 '18 at 17:25
  • 2
    how would you re-render the marker every time the map changes? – frogbandit Jul 12 '18 at 01:28
  • is their a demo – RussellHarrower Dec 28 '20 at 09:46
20

Adding a marker on your map isn't as easy as we would like to, mostly because the confusing docs but here you have a super easy example:

const Map = props => {
  return (
    <GoogleMapReact
     bootstrapURLKeys={{ props.key }}
     defaultCenter={{lat: props.lat, lng: props.lng}}
     defaultZoom={props.zoom}>

       {/* This is the missing part in docs:
         *
         * Basically, you only need to add a Child Component that
         * takes 'lat' and 'lng' Props. And that Component should
         * returns a text, image, super-awesome-pin (aka, your marker).
         *
         */}
       <Marker lat={props.lat} lng={props.lng}} />
    </GoogleMapReact>
  )
}

const Marker = props => {
  return <div className="SuperAwesomePin"></div>
}
Jobsamuel
  • 1,342
  • 1
  • 13
  • 25
13
import React from 'react';
import GoogleMapReact from 'google-map-react';

const GoogleMaps = ({ latitude, longitude }) => {
 const renderMarkers = (map, maps) => {
  let marker = new maps.Marker({
  position: { lat: latitude, lng: longitude },
  map,
  title: 'Hello World!'
  });
  return marker;
 };

 return (
   <div style={{ height: '50vh', width: '100%' }}>
    <GoogleMapReact
      bootstrapURLKeys={{ key: 'YOUR KEY' }}
      defaultCenter={{ lat: latitude, lng: longitude }}
      defaultZoom={16}
      yesIWantToUseGoogleMapApiInternals
      onGoogleApiLoaded={({ map, maps }) => renderMarkers(map, maps)}
    >
    </GoogleMapReact>
   </div>
 );
};

export default GoogleMaps;
Azmol
  • 399
  • 4
  • 6
  • Looking in the docs, it mentions 'You can access to Google Maps map and maps objects by using onGoogleApiLoaded, in this case you will need to set yesIWantToUseGoogleMapApiInternals to true', this helped me with the above code which worked for me. – Azmol Nov 01 '19 at 09:45
1

Based on the answer by MasterAM.

If you using react hooks, this one could do the trick:

import React, { useMemo, useEffect } from 'react';

const createControlledPromise = () => {
  let resolver;
  let rejector;
  const promise = new Promise((resolve, reject) => {
    resolver = resolve;
    rejector = reject;
  });
  return { promise, resolver, rejector };
};

const useMarker = ({ lat, lng }) => {
  const { 
    promise: apiPromise, 
    resolver: handleGoogleApiLoaded 
  } = useMemo(
    createControlledPromise,
    []
  ).current;

  useEffect(
    () => {
      let marker;
      apiPromise.then(api => {
        const { map, maps } = api;
        marker = new maps.Marker({ position: { lat, lng }, map });
      });
      return () => {
        if (marker) {
          marker.setMap(null);
        }
      };
    },
    [lat, lon],
  );
  return { handleGoogleApiLoaded };
};

const Location = ({
  location: { locationName, lat, lng }
}) => {
  const { handleGoogleApiLoaded } = useMarker({ lat, lng });
  return (
    <section>
      <h1>{locationName}</h1>
      <p>
        <GoogleMapReact
          bootstrapURLKeys={{ key: __GOOGLE_MAP_API_KEY__ }}
          center={{ lat, lng }}
          defaultZoom={11}
          onGoogleApiLoaded={handleGoogleApiLoaded}
        />
      </p>
    </section>
  );
};
Gleb Lobastov
  • 161
  • 1
  • 4
  • Thanks for the mention. Note that the API/docs have changed to support children. See my edit for more details. – MasterAM Jan 04 '21 at 13:28