Skip to content

Commit

Permalink
fix: infowindow double rendering and eslint warnings (#185)
Browse files Browse the repository at this point in the history
* infowindow: fix double rendering issue  (#109, #175)
* hooks: resolve eslint warnings considering missing dependencies
  • Loading branch information
mrMetalWood authored Jan 31, 2024
1 parent e8c88ab commit 404cc06
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 16 deletions.
11 changes: 11 additions & 0 deletions src/components/advanced-marker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,19 @@ function useAdvancedMarker(props: AdvancedMarkerProps) {
setMarker(null);
setContentContainer(null);
};
// We do not want to re-render the whole marker when the className changes
// because that causes a short flickering of the marker.
// The className update is handled in the useEffect below.
// Excluding the className from the dependency array onm purpose here
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [map, markerLibrary, numChilds]);

// update className of advanced marker element
useEffect(() => {
if (!contentContainer) return;
contentContainer.className = className ?? '';
}, [contentContainer, className]);

// bind all marker events
useEffect(() => {
if (!marker) return;
Expand Down
70 changes: 57 additions & 13 deletions src/components/info-window.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
/* eslint-disable complexity */
import React, {PropsWithChildren, useContext, useEffect, useState} from 'react';
import React, {
PropsWithChildren,
useContext,
useEffect,
useRef,
useState
} from 'react';
import {createPortal} from 'react-dom';

import {GoogleMapsContext} from './map';
Expand All @@ -19,38 +25,76 @@ export const InfoWindow = (props: PropsWithChildren<InfoWindowProps>) => {
const {children, anchor, onCloseClick, ...infoWindowOptions} = props;
const map = useContext(GoogleMapsContext)?.map;

const infoWindowRef = useRef<google.maps.InfoWindow | null>(null);
const [contentContainer, setContentContainer] =
useState<HTMLDivElement | null>(null);

// create infowindow once map is available
useEffect(() => {
if (!map) return;

const infoWindow = new google.maps.InfoWindow(infoWindowOptions);
const newInfowindow = new google.maps.InfoWindow(infoWindowOptions);

// Add content to info window
const el = document.createElement('div');
infoWindow.setContent(el);
infoWindow.open({map, anchor});

if (onCloseClick) {
google.maps.event.addListener(infoWindow, 'closeclick', () => {
onCloseClick();
});
}
newInfowindow.setContent(el);

infoWindowRef.current = newInfowindow;
setContentContainer(el);

// Cleanup info window and event listeners on unmount
return () => {
google.maps.event.clearInstanceListeners(infoWindow);
google.maps.event.clearInstanceListeners(newInfowindow);

infoWindow.close();
newInfowindow.close();
el.remove();

setContentContainer(null);
};
}, [map, children, anchor]);
// We don't want to re-render a whole new infowindow
// when the options change to prevent flickering.
// Update of infoWindow options is handled in the useEffect below.
// Excluding infoWindowOptions from dependency array on purpose here.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [map, children]);

// Update infoWindowOptions
useEffect(() => {
infoWindowRef.current?.setOptions(infoWindowOptions);
}, [infoWindowOptions]);

// Handle the close click callback
useEffect(() => {
if (!infoWindowRef.current) return;

let listener: google.maps.MapsEventListener | null = null;

if (onCloseClick) {
listener = google.maps.event.addListener(
infoWindowRef.current,
'closeclick',
onCloseClick
);
}

return () => {
if (listener) listener.remove();
};
}, [onCloseClick]);

// Open info window after content container is set
useEffect(() => {
// anchor === null means an anchor is defined but not ready yet.
if (!contentContainer || !infoWindowRef.current || anchor === null) return;

const openOptions: google.maps.InfoWindowOpenOptions = {map};

if (anchor) {
openOptions.anchor = anchor;
}

infoWindowRef.current.open(openOptions);
}, [contentContainer, infoWindowRef, anchor, map]);

return (
<>{contentContainer !== null && createPortal(children, contentContainer)}</>
Expand Down
2 changes: 1 addition & 1 deletion src/components/map-control.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export const MapControl = ({children, position}: MapControlProps) => {
const index = controls.getArray().indexOf(controlContainer);
controls.removeAt(index);
};
}, [map, position]);
}, [controlContainer, map, position]);

return createPortal(children, controlContainer);
};
4 changes: 4 additions & 0 deletions src/components/marker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ function useMarker(props: MarkerProps) {
newMarker.setMap(null);
setMarker(null);
};
// We do not want to re-render the whole marker when the options change.
// Marker options update is handled in a useEffect below.
// Excluding markerOptions from dependency array on purpose here.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [map]);

// attach and re-attach event-handlers when any of the properties change
Expand Down
2 changes: 1 addition & 1 deletion src/components/pin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export const Pin = (props: PropsWithChildren<PinProps>) => {

// Set content of Advanced Marker View to the Pin View element
advancedMarker.content = pinElement.element;
}, [advancedMarker, props]);
}, [advancedMarker, glyphContainer, props]);

return createPortal(props.children, glyphContainer);
};
2 changes: 1 addition & 1 deletion src/hooks/use-maps-library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export function useMapsLibrary(name: string) {
// The returned promise is ignored, since importLibrary will update loadedLibraries
// list in the context, triggering a re-render.
void ctx.importLibrary(name);
}, [apiIsLoaded, ctx?.importLibrary]);
}, [apiIsLoaded, ctx, name]);

return ctx?.loadedLibraries[name] || null;
}

0 comments on commit 404cc06

Please sign in to comment.