Skip to content

Commit

Permalink
fix(info-window): fix reappearing InfoWindows (#393)
Browse files Browse the repository at this point in the history
An InfoWindow that is attached to a Marker or AdvancedMarker as anchor would occasionally show up again after being unmounted. This can happen when the anchor is removed from the map before the InfoWindow is unmounted but gets re-added to the map at a later point.

This is caused by intended behavior in the maps API, and the only workaround for this right now is to forcefully disconnect the InfoWindow from its anchor.

See here for more details: https://issuetracker.google.com/issues/343750849

Co-Authored-By: Nick Schnelle nick.schnelle@bupa.com.au
  • Loading branch information
usefulthink authored May 30, 2024
1 parent 3960343 commit dc51eb9
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 9 deletions.
25 changes: 17 additions & 8 deletions src/components/info-window.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,23 +69,21 @@ export const InfoWindow = (props: PropsWithChildren<InfoWindowProps>) => {
// intentionally shadowing the state variables here
const infoWindow = new google.maps.InfoWindow(infoWindowOptions);
const contentContainer = document.createElement('div');

infoWindow.setContent(contentContainer);

setInfoWindow(infoWindow);
setContentContainer(contentContainer);

// cleanup: remove infoWindow, all event listeners and contentElement
// unmount: remove infoWindow and contentElement
return () => {
google.maps.event.clearInstanceListeners(infoWindow);

infoWindow.close();
infoWindow.setContent(null);
contentContainer.remove();

setInfoWindow(null);
setContentContainer(null);
};
},
// `infoWindowOptions` is missing from dependencies:
// `infoWindowOptions` and `pixelOffset` are missing from dependencies:
//
// We don't want to re-create the infowindow instance
// when the options change.
Expand Down Expand Up @@ -121,7 +119,8 @@ export const InfoWindow = (props: PropsWithChildren<InfoWindowProps>) => {
infoWindow.setOptions(infoWindowOptions);
},

// dependency `infoWindow` isn't needed since options are passed to the constructor
// dependency `infoWindow` isn't needed since options are also passed
// to the constructor when a new infoWindow is created.
// eslint-disable-next-line react-hooks/exhaustive-deps
[infoWindowOptions]
);
Expand All @@ -136,8 +135,8 @@ export const InfoWindow = (props: PropsWithChildren<InfoWindowProps>) => {
// `anchor === null` means an anchor is defined but not ready yet.
if (!contentContainer || !infoWindow || anchor === null) return;

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

if (anchor) {
openOptions.anchor = anchor;
}
Expand All @@ -147,6 +146,16 @@ export const InfoWindow = (props: PropsWithChildren<InfoWindowProps>) => {
}

infoWindow.open(openOptions);

return () => {
// Note: when the infowindow has an anchor, it will automatically show up again when the
// anchor was removed from the map before infoWindow.close() is called but the it gets
// added back to the map after that.
// More information here: https://issuetracker.google.com/issues/343750849
if (isOpenedWithAnchor) infoWindow.set('anchor', null);

infoWindow.close();
};
}, [infoWindow, contentContainer, anchor, map, shouldFocus]);

return (
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/use-maps-event-listener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ export function useMapsEventListener<T extends (...args: any[]) => void>(

const listener = google.maps.event.addListener(target, name, callback);

return () => listener?.remove();
return () => listener.remove();
}, [target, name, callback]);
}

0 comments on commit dc51eb9

Please sign in to comment.