0

I'm looking for a way to use the same google map instance anywhere in my app. Each map load is charged after all...

I'm using google-map-react. A new Map instance is created on ComponentDidMount, so to me it makes sense that the 1st requirement would be to keep that component mounted, most likely near the top of the component Tree.

The end result should be that any component that wants to render the map should:

  • Mount the map component if it still hasn't been mounted.
  • Somehow recycle the DOM node where the map is mounted at.

Any help with this will be appreciated.

2 Answers2

2

Portals were indeed the right direction. I came up with the following approach:

  1. 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>
  1. 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"));
  1. 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);
      }
    }
  1. 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.

1

You can try to implement it by using Portals

Portals provide a first-class way to render children into a DOM node that exists outside the DOM hierarchy of the parent component.

However, every change the user makes to the map (zoom,layers,etc) will remain, so it would make sense you also reset the map to the initial state whenever you render the map in another section.

Albondi
  • 1,141
  • 1
  • 8
  • 19