How to draw Polyline with google-map-react?

4.6k views Asked by At

Everyone I am using google-map-react and not react-google-map. Can you help me drawing a line between two points.

          <div className='videoDetails__map'>
            <GoogleMapReact
              bootstrapURLKeys={{
                key: "",
              }}
              center={{
                lat: lat,
                lng: lng,
              }}
              defaultZoom={15}
            >
              <AnyReactComponent lat={lat} lng={lng} text='My Marker' />
            </GoogleMapReact>
          </div>
2

There are 2 answers

1
Saul Muñoz Garcia On

To place a polyline on your map, set <Polyline /> as child of Map component

render() {
  const someCoords= [
    {lat: 32.321, lng: -64.757},
    {lat: 25.774, lng: -80.190}
  ];
 
  return(
    <div className='videoDetails__map'>
         <GoogleMapReact
           bootstrapURLKeys={{
             key: "",
           }}
           center={{
             lat: lat,
             lng: lng,
           }}
           defaultZoom={15}>
           <Polyline
              path={someCoords}
              strokeColor="#0000FF"
              strokeOpacity={0.8}
              strokeWeight={2} />
           <AnyReactComponent lat={lat} lng={lng} text='My Marker' />
         </GoogleMapReact>
    </div>
  )
}

Here you can find the documentation

0
Samwise On

I've been struggling with this question (and the various unmaintained React wrappers for Google Maps) for about a week, and finally buckled down to build my own React wrapper for google.maps.Polyline by using the useEffect hook to copy the component props into the Google Polyline object.

I still consider myself a React novice so this was a good learning experience, and since nobody else has offered an answer I'm sharing what I came up with in the hope that somebody else will tell both me and OP a better way:

import useDeepCompareEffect from 'use-deep-compare-effect';
import { useState } from 'react';

// bonkers IMO that this isn't a method on MVCArray, but here we are
function pathsDiffer(
    path1: google.maps.MVCArray<google.maps.LatLng>,
    path2: google.maps.LatLngLiteral[]
): boolean {
    if (path1.getLength() != path2.length)
        return true;
    for (const [i, val] of path2.entries())
        if (path1.getAt(i).toJSON() != val)
            return true;
    return false;
}

// React component for map polyline.
export function PolyLine(props: {
    map: google.maps.Map|null,
    path: google.maps.LatLngLiteral[]
}) {
    const [polyline, setPolyline] = useState<google.maps.Polyline|null>(null);
    useDeepCompareEffect(() => {
        // Create polyline after map initialized.
        if (!polyline && props.map)
            setPolyline(new google.maps.Polyline);

        // Synchronize map polyline with component props.
        if (polyline && polyline.getMap() != props.map)
            polyline.setMap(props.map);
        if (polyline && pathsDiffer(polyline.getPath(), props.path))
            polyline.setPath(props.path);

        return () => {
            // Cleanup: remove line from map
            if (polyline)
                polyline.setMap(null);
        }
    }, [props, polyline]);

    return null;
}

Some important subtleties:

  • I'm using useDeepCompareEffect because I want to be able to specify props as a dependency and trigger the effect any time the individual props or the path vertices change. Regular old useEffect would work fine as long as the individual props were specified as dependencies and the path was rendered into something like JSON or we made very certain never to mutate the path array or re-create it unnecessarily.
  • The state variable polyline is specified as a dependency so that after I set it inside the effect and it triggers a re-render, the effect will run again (this time with the actual polyline). Normally you want to avoid having your effect set something that will trigger another effect run, but in this case we only set the polyline once so we avoid an infinite effect loop. Doing it this roundabout way is necessary because we need to wait for the API to load (which is signified by the arrival of our map prop) before we can create a Polyline.

I then set this component as a sibling (not child) of GoogleMapReact like so:

    const [map, setMap] = useState<google.maps.Map|null>(null);
    return (
        <div style={{width: '95vw', height: '53vw', margin: '1vw'}}>
            <GoogleMapReact
                bootstrapURLKeys={{ key: "my key" }}
                defaultCenter={{lat: 0, lng: 0}}
                defaultZoom={5}
                yesIWantToUseGoogleMapApiInternals
                onGoogleApiLoaded={
                    (maps: {map: google.maps.Map, maps: any}) => {
                        setMap(maps.map)
                    }
                }
            />
            <PolyLine
                map={map}
                path={POLYLINE_PATH}
            />
        </div>
    );

Adding support for other polyline properties (stroke options etc) is left as an exercise for the reader.