Search code examples
leafletreact-leaflet

React Leaflet: How to get access to the Map object after first render?


I have a parent component with two children:

  1. a Leaflet map
  2. a list of locations visible on the map (calculated using the map's getBounds())

I'm having a problem initializing that list of locations. Some things I tried:

  • I create a reference using useRef in the parent of the two components and passing it the MapContainer via the ref tag but it isn't initialized by the time the parent's useEffect happens.
  • I created a callback which I tried calling in whenReady to pass the bounds back to the parent, but whenReady doesn't have access to the Map Object.
  • I created a child of the MapContainer to handle events. That child reacts to 'load' and 'moveend' and that works for when the map moves but doesn't run on first render.
  • I tried having the event handler component call a callback in the parent to trigger the redraw. But that causes a component update during a component update which isn't allowed.

So how do I trigger a callback in the parent with the bounds of the map after it is finished loading?


Solution

  • When working with react leaflet I think its easier to create components that you can place within your MapContainer that can handle specific tasks e.g. Bounds

    Here is how I get the bounds of the map:

    const Bounds: React.FC<{ onMove: (bounds: MapBounds) => void }> = ({
      onMove,
    }) => {
      const map = useMapEvent("moveend", (event) => {
        onMove(convertBounds(event.sourceTarget.getBounds()));
      });
    
      const convertBounds = (bounds: any) => {
        const southWest = bounds.getSouthWest();
        const northEast = bounds.getNorthEast();
    
        return {
          min: {
            lat: southWest.lat,
            lng: southWest.lng,
          },
          max: {
            lat: northEast.lat,
            lng: northEast.lng,
          },
        };
      };
    
      // set inital bounds, important that onMove is not changed because that wil cause loop
      useEffect(() => {
        onMove(convertBounds(map.getBounds()));
      }, [onMove, map]);
    
      return <></>;
    };
    

    Then You can just add <Bounds onMove={onBoundsChange} /> as a child to MapContainer.