0

While using react-leaflet to create a map, I would like the map component to take over all the available space after header and footer. The code seems to work. However, when the resize handler is triggered, the map component size didn't change. Any suggested fixes?

CodeSandbox link at the bottom.

import React, { useState, useEffect, useRef } from "react";
import { MapContainer, TileLayer } from "react-leaflet";
import "leaflet/dist/leaflet.css";

const MyMap = () => {
  const mapRef = useRef(null);
  const [windowHeight, setWindowHeight] = useState(150);
  const [loading, setLoading] = useState(true);
  useEffect(() => {
    const handleResize = () => {
      const headerHeight = document.querySelector("header").offsetHeight;
      const footerHeight = document.querySelector("footer").offsetHeight;
      console.log(
        window.innerHeight,
        headerHeight,
        document.querySelector("header"),
        footerHeight
      );
      setWindowHeight(window.innerHeight - headerHeight - footerHeight - 16);
      if (mapRef) {
        console.log("map exist", windowHeight, mapRef.current);
      }
      setLoading(false);
    };
    window.setTimeout(handleResize, 1000);
    window.addEventListener("resize", handleResize);
    return () => {
      window.removeEventListener("resize", handleResize);
    };
  }, []);
  return (
    <div style={{ display: "flex", flexDirection: "column" }}>
      <header>
        <h1>HEADER</h1>
      </header>
      {loading && <div>loading...</div>}
      <main style={{ flex: "1 1 auto" }}>
        {!loading && (
          <MapContainer
            center={[51.505, -0.09]}
            zoom={13}
            style={{ height: `${windowHeight}px` }}
            ref={mapRef}
          >
            <TileLayer
              url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
              attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
            />
          </MapContainer>
        )}
      </main>
      <footer>
        <h1>Footer</h1>
      </footer>
    </div>
  );
};

export default MyMap;

https://codesandbox.io/embed/lknkgg?view=editor+%2B+preview&module=%2Fsrc%2FMyApp.js

1 Answer 1

1

Per the documentation Except for its children, MapContainer props are immutable: changing them after they have been set a first time will have no effect on the Map instance or its container.

This means changing the height in styles will not have any effect. A quick and dirty solution is to force a rerender by setting a key e.g.

<MapContainer
  center={[51.505, -0.09]}
  zoom={13}
  style={{ height: `${windowHeight}px` }}
  key={windowHeight}
  ref={mapRef}
>

However I would not recommend this. I did go trough my old code an found how I solved it:

A child component to MapContainer can access the map, from which it can invalidate the size.

<MapContainer
  center={position ? position : [61, 15]}
  zoom={zoom}
  scrollWheelZoom={true}
  style={{ height: height, width: "100%", zIndex: 94 }}
  minZoom={4}
  maxZoom={18}
  preferCanvas
>
  <MapReziser width={rect.width} height={rect.height} />
.
.
.
</MapContainer>

MapReziser should be a component that have access to the map an invalidates the size when height changes.

const MapReziser: React.FC<{ width: number; height: number }> = ({
  width,
  height,
}) => {
  const map = useMap();
  useEffect(() => {
    if (width > 0 && height > 0) {
      // If size is changed the map will have render issues
      // e.g. two maps in different tabs or a map in a accordion
      // To solve this we monitor the size of the map and invalidate the size on change
      // This will cause the map to rerender and fix the issue
      // However this will cause issues with open popups
      // To solve this we fly to the current center of the map
      map.invalidateSize();
      map.flyTo(map.getCenter());
    }
  }, [map, width, height]);
  return null;
};

Remember to adjust the code for your project and best of luck

1
  • I think you are right. Using the suggest code, the map still doesn't resize. But removing the element and re-render do work. As you mentioned, MapContainer props are immutable.
    – obvdso
    Commented May 24 at 19:01

Not the answer you're looking for? Browse other questions tagged or ask your own question.