I have a React v18.2 (TS) application where I need to display Google maps to my users. I need the drawing functionality, a.k.a. geofence
so my users can draw polygon shapes on the map. I have integrated the map using @react-google-maps/api
v2.18.1 and everything works perfect IF I am NOT using React.StrictMode
. Can someone help me with this issue? I need my React.StrictMode
turned ON and still have the Geofence UI rendered on my maps so my users can use it. How is this related to React strict mode and is there a way to fix this?
This is my Map
component by the way:
import { Polygon, GoogleMap, useLoadScript } from '@react-google-maps/api';
import { DrawingManager } from './drawing-manager';
interface MapsProps {
lat: number;
lng: number;
isStatic: boolean;
zoom?: number;
isDrawable?: boolean;
}
interface Shape {
type: string;
path: google.maps.LatLngLiteral[];
radius?: number;
}
const { useRef, useMemo, useState, useCallback } = React;
const __mapMandatoryStyles = { width: '100%', height: '100%' }; // ⌂ Keep it like this and style the parent if needed!
const googleMapsApiKey = process.env.REACT_APP_GOOGLE_MAPS_API_KEY as string;
const libraries: ('drawing' | 'geometry' | 'localContext' | 'places' | 'visualization')[] = [
'drawing',
]; // ⌂ Do NOT touch this type! It comes from the '@react-google-maps/api' lib but it's not exported from there.
export const Map = ({
lat,
lng,
isStatic,
zoom = 15,
isDrawable = false,
}: MapsProps): JSX.Element | null => {
const [shapes, setShapes] = useState<Array<Shape>>([]);
const { isLoaded } = useLoadScript({
id: 'google-map-script',
googleMapsApiKey,
libraries,
});
const mapRef = useRef<google.maps.Map | null>(null);
const drawingManagerRef = useRef(null);
const handleMapLoad = useCallback((map: google.maps.Map) => {
if (mapRef?.current) {
mapRef.current = map;
}
}, []);
const handleDraw = useCallback(
(shape: Shape) => {
setShapes([...shapes, shape]);
},
[shapes]
);
const handleOnPolygonComplete = useCallback(
(polygon: google.maps.Polygon) => {
if (polygon.getPath() !== null) {
const polygonPathArray = polygon.getPath().getArray();
if (polygonPathArray) {
const path = polygonPathArray.map(latLngLiteral => {
return { lat: latLngLiteral.lat(), lng: latLngLiteral.lng() };
});
handleDraw({ path, type: 'polygon' });
}
}
},
[handleDraw]
);
const renderShapes = useCallback(
() =>
shapes.map((shape, idx): JSX.Element | null =>
shape.type === 'polygon' ? (
<Polygon key={idx} path={shape.path.map(latLng => latLng)} />
) : null
),
[shapes]
);
const getMapOptions = useMemo(
() => ({
zoomControl: !isStatic,
scrollwheel: !isStatic,
rotateControl: !isStatic,
clickableIcons: !isStatic,
mapTypeControl: !isStatic,
keyboardShortcuts: !isStatic,
fullscreenControl: !isStatic,
streetViewControl: !isStatic,
disableDoubleClickZoom: isStatic,
isFractionalZoomEnabled: !isStatic,
gestureHandling: isStatic ? 'none' : 'auto',
}),
[isStatic]
);
if (isDrawable) {
return isLoaded ? (
<GoogleMap
zoom={zoom}
center={{ lat, lng }}
options={getMapOptions}
mapContainerStyle={__mapMandatoryStyles}
onLoad={handleMapLoad}
>
<DrawingManager
ref={drawingManagerRef}
options={{
drawingControl: true,
drawingControlOptions: {
position: google.maps.ControlPosition.TOP_CENTER,
drawingModes: [google?.maps?.drawing?.OverlayType.POLYGON],
},
}}
onPolygonComplete={handleOnPolygonComplete}
/>
{renderShapes()}
</GoogleMap>
) : null;
}
return isLoaded ? (
<GoogleMap
zoom={zoom}
center={{ lat, lng }}
options={getMapOptions}
mapContainerStyle={__mapMandatoryStyles}
/>
) : null;
};
The DrawingManager
's code is:
import * as React from 'react';
import {
DrawingManager as RactGoogleMapsDrawingManager,
DrawingManagerProps,
} from '@react-google-maps/api';
export const DrawingManager = React.forwardRef<RactGoogleMapsDrawingManager, DrawingManagerProps>(
(props, ref): JSX.Element => <RactGoogleMapsDrawingManager ref={ref} {...props} />
);
DrawingManager.displayName = 'DrawingManager';