Home > other >  Trying to apply zoom on click with react-leaflet
Trying to apply zoom on click with react-leaflet

Time:02-11

I'm trying to replicate some functions of the leaflet map https://leafletjs.com/examples/choropleth/ to react/typeScript but I'm getting an error when I try to add the function to zoom on a Feature it breaks, this is the code I'm working on

import Leaflet from 'leaflet';
import React, {
  useRef, RefObject,
} from 'react';
import {
  MapContainer, TileLayer, GeoJSON, useMap,
} from 'react-leaflet';
import styled from 'styled-components';

import { statesData } from './statesData';
import './style.css';

const EmployerLocationMap = () => {
  const geoJson: RefObject<Leaflet.GeoJSON> = useRef(null);
  const map = useMap();

  const highlightFeature = (e: Leaflet.LeafletMouseEvent) => {
    const layer = e.target;

    layer.setStyle({
      color: '#666',
      dashArray: '',
      fillOpacity: 0.7,
      weight: 5,
    });
  };

  const resetHighlight = (e: Leaflet.LeafletMouseEvent) => {
    geoJson.current?.resetStyle(e.target);
  };

  const zoomToFeature = (e: Leaflet.LeafletMouseEvent) => {
    map.fitBounds(e.target.getBounds());
  };

  return (
    <CardContainer>
      <Row>
        <Col width='60%'>
          <MapContainer
            center={[37.8, -96]}
            zoom={3}
          >
            <TileLayer
              attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
              url='https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
            />
            <GeoJSON
              data={statesData}
              key='usa-states'
              ref={geoJson}
              style={() => {
                return {
                  color: 'white',
                  dashArray: '3',
                  fillColor: '#f0f0f0',
                  fillOpacity: 0.7,
                  opacity: 1,
                  weight: 2,
                };
              }}
              onEachFeature={(__, layer) => {
                layer.on({
                  click: (e) => {
                    zoomToFeature(e);
                  },
                  mouseout: (e) => {
                    resetHighlight(e);
                  },
                  mouseover: (e) => {
                    highlightFeature(e);
                  },
                });
              }}
            />
          </MapContainer>
        </Col>
      </Row>

    </CardContainer>
  );
};

export default EmployerLocationMap;

and when I start the frontend i get this error on the console:

Uncaught Error: No context provided: useLeafletContext() can only be used in a descendant of <MapContainer>

at useLeafletContext

I don't get what could be wrong, I've seen several implementations on this and it looks the same.

CodePudding user response:

useMap can only be used within a component that is itself a child/descendant of <MapContainer>. https://react-leaflet.js.org/docs/api-map/#usemap

Hook providing the Leaflet Map instance in any descendant of a MapContainer.

Here you try calling useMap in your EmployerLocationMap component, which contains a <MapContainer>, but which is obviously not a descendant of one.

A solution for your case could be to separate the map logic in another component that you can place inside the <MapContainer>:

const EmployerLocationMap = () => {
  return (
    <CardContainer>
      <Row>
        <Col width='60%'>
          <MapContainer
            center={[37.8, -96]}
            zoom={3}
          >
            <TileLayer
              attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
              url='https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
            />
            <MapContent />
          </MapContainer>
        </Col>
      </Row>

    </CardContainer>
  );
};

const MapContent = () => {
  const geoJson: RefObject<Leaflet.GeoJSON> = useRef(null);
  const map = useMap();

  const highlightFeature = (e: Leaflet.LeafletMouseEvent) => {
    const layer = e.target;

    layer.setStyle({
      color: '#666',
      dashArray: '',
      fillOpacity: 0.7,
      weight: 5,
    });
  };

  const resetHighlight = (e: Leaflet.LeafletMouseEvent) => {
    geoJson.current?.resetStyle(e.target);
  };

  const zoomToFeature = (e: Leaflet.LeafletMouseEvent) => {
    map.fitBounds(e.target.getBounds());
  };

  return (
            <GeoJSON
              data={statesData}
              key='usa-states'
              ref={geoJson}
              style={() => {
                return {
                  color: 'white',
                  dashArray: '3',
                  fillColor: '#f0f0f0',
                  fillOpacity: 0.7,
                  opacity: 1,
                  weight: 2,
                };
              }}
              onEachFeature={(__, layer) => {
                layer.on({
                  click: (e) => {
                    zoomToFeature(e);
                  },
                  mouseout: (e) => {
                    resetHighlight(e);
                  },
                  mouseover: (e) => {
                    highlightFeature(e);
                  },
                });
              }}
            />
  );
};
  • Related