From where is leaflet loading data after hot module reload?

Viewed 191

I'm using react-leaflet, and after webpack triggers a hot reload, GeoJSON features remain on the map, but none are present if we check map._layers on load, like this:

map.whenReady(() => console.log(map._layers))

Similarly, using map.eachLayer doesn't work, since leaflet apparently doesn't yet know about the layers.

But if I open the Chrome console and check map._layers manually, the layers are present. Why is this delay happening, and how can I wait for them to load so I can clear them?

1 Answers

If you take a look at the React-Leaflet docs, you can see that the whenReady method doesn't take in the map instance. The whenCreated method, however, does:

React-Leaflet Documentation for MapContainer

If you want to access the map instance once the map has been rendered, you should be using the whenCreated map method rather than the whenReady method.

You can also use the whenCreated method to set the map to state and access it in a useEffect hook, for eaxmple. Here is an example component:

import React, { useState, useEffect } from 'react';
import L from 'leaflet';
import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet';

const MapComponent = () => {
  const [map, setMap] = useState(null);
  const position = [51.505, -0.09];

  useEffect(() => {
    if (!map) return;
    if (map) {
      console.log(map.eachLayer((layer) => console.log('USE EFFECT:', layer)));
    }
  }, [map]);

  return (
    <>
      <MapContainer
        center={position}
        zoom={13}
        scrollWheelZoom={false}
        style={{ height: '100vh', width: '100%', padding: 0 }}
        whenCreated={(map) => {
          console.log('MAP:', map);
          console.log(map.eachLayer((layer) => console.log('LAYER:', layer)));
          setMap(map);
        }}
      >
        <TileLayer
          attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
          url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
        />
        <Marker position={position}>
          <Popup>
            A pretty CSS3 popup. <br /> Easily customizable.
          </Popup>
        </Marker>
      </MapContainer>
    </>
  );
};

export default MapComponent;

Here is a demo on StackBlitz you can play around with: DEMO


If you want to access the map instance from the useMap or any other hook, you just have to make sure that you are using the hook in a descendant of a MapContainer:

import React, { useState, useEffect } from 'react';
import L from 'leaflet';
import { MapContainer, TileLayer, Marker, Popup, useMap } from 'react-leaflet';

const ChildOfMapContainer = (props) => {
  // Set up the useMap hook in the descendant of MapContainer
  const map = useMap();

  // Access the map instance:
  useEffect(() => {
    // map.eachLayer((layer) => console.log('Layer:', layer));
    map.whenReady(() => map.eachLayer((layer) => console.log(layer)));
  }, [props, map]);

  return (
    <>
      <TileLayer
        attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
        url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
      />
      <Marker position={props.position}>
        <Popup>
          A pretty CSS3 popup. <br /> Easily customizable.
        </Popup>
      </Marker>
    </>
  );
};


const MapComponent = () => {
  const position = [51.505, -0.09];
  return (
    <>
      <MapContainer
        center={position}
        zoom={13}
        scrollWheelZoom={false}
        style={{ height: '100vh', width: '100%', padding: 0 }}
      >
        <ChildOfMapContainer position={position} />
      </MapContainer>
    </>
  );
};

export default MapComponent;

Sandbox with this example

Related