Portals were indeed the right direction. I came up with the following approach:
- Add a sibling node to index.html:
<!-- Root node of React app -->
<div id="root"></div>
<!-- Keep the map here while not in use -->
<div id="map-bench">
<!-- Create the portal using this node as container (2nd argument) -->
<div id="map-container" style="height: 100%; width: 100%"></div>
</div>
- Make a component that renders the map inside a portal, and then mount it somewhere near the top of tree; the whole point is to keep the map from unmounting.
return createPortal(renderMap(), document.getElementById("map-container"));
- Use a callback ref to plug the map wherever you need it. How you actually use it on your components is up to you; I made a hook that returns this function.
const registerMapOutlet = (rootNode: HTMLDivElement | null) => {
// THE MAP
const mapNode = document.getElementById("map-container");
// Bail out if map node is not found
if (mapNode === null) return;
// on mount: Attach map
if (rootNode) {
rootNode.replaceWith(mapNode);
console.log("REGISTERED MAP OUTLET");
// GoogleMapReact won't load/mount until it is first used
dispatch(mountMapIfNeeded());
}
// on unmount: Return map to bench node
else {
const mapBenchNode = document.getElementById("map-bench");
if (mapBenchNode === null) {
console.warn("Map Bench node was not found");
return;
}
mapBenchNode.appendChild(mapNode);
}
}
- Finally, use the map on any component like this:
// You can pass GoogleMapReact props to this hook
const { registerMapOutlet } = useMapOutlet({});
return <div ref={registerMapOutlet}></div>
Version 1.1.5 of google-map-react introduced a shouldUnregisterMapOnUnmount prop. I will experiment with it soon and update my answer.