Home > Mobile >  React-Leaflet LocateControl component - keeps duplicating on each refresh
React-Leaflet LocateControl component - keeps duplicating on each refresh

Time:11-29

I'm using Locate Control in React Leaflet, but the Locate Control buttons are always duplicated, and sometimes I get 3 or 4 of them (see image below). I'm running the function through a useEffect with empty dependency to only fire it once, but no matter. I can target the class with display: none, but then both disappear. I feel like this might be an issue with Locate Control library? Really not sure. Open to any help or ideas.

import { useEffect } from "react"
import { useMap } from "react-leaflet"
import Locate from "leaflet.locatecontrol"
import "leaflet.locatecontrol/dist/L.Control.Locate.min.css"

const AddLocate = () => {
  const map = useMap()

  useEffect(() => {
    const locateOptions = {
      position: "bottomleft",
      flyTo: true,
    }
    const locateControl = new Locate(locateOptions)
    locateControl.addTo(map)
  }, [])

  return null
}

export default AddLocate;

Duplicated locate control

CodePudding user response:

I would do some debugging to that useEffect to see if it's only happening once. It's possible the entire component is mounted multiple times.

CodePudding user response:

Looks like you use a package made for leaflet. Which should for the most parts be okay. However the way you add the control is not really the react-leaflet way, where we want to add add components rather than add "stuff" directly to the map.

Below you can see how easy it is to implement a location component that you simply just can add as component within your MapContainer.

import { ActionIcon } from "@mantine/core";
import React, { useState } from "react";
import { useMapEvents } from "react-leaflet";
import { CurrentLocation } from "tabler-icons-react";
import LeafletControl from "./LeafletControl";

interface LeafletMyPositionProps {
  zoom?: number;
}

const LeafletMyPosition: React.FC<LeafletMyPositionProps> = ({ zoom = 17 }) => {
  const [loading, setLoading] = useState<boolean>(false);
  const map = useMapEvents({
    locationfound(e) {
      map.flyTo(e.latlng, zoom);
      setLoading(false);
    },
  });

  return (
    <LeafletControl position={"bottomright"}>
      <ActionIcon
        onClick={() => {
          setLoading(true);
          map.locate();
        }}
        loading={loading}
        variant={"transparent"}
      >
        <CurrentLocation />
      </ActionIcon>
    </LeafletControl>
  );
};

export default LeafletMyPosition;

And for LeafletControl I just have this reusable component:

import L from "leaflet";
import React, { useEffect, useRef } from "react";

const ControlClasses = {
  bottomleft: "leaflet-bottom leaflet-left",
  bottomright: "leaflet-bottom leaflet-right",
  topleft: "leaflet-top leaflet-left",
  topright: "leaflet-top leaflet-right",
};

type ControlPosition = keyof typeof ControlClasses;
export interface LeafLetControlProps {
  position?: ControlPosition;
  children?: React.ReactNode;
}

const LeafletControl: React.FC<LeafLetControlProps> = ({
  position,
  children,
}) => {
  const divRef = useRef(null);

  useEffect(() => {
    if (divRef.current) {
      L.DomEvent.disableClickPropagation(divRef.current);
      L.DomEvent.disableScrollPropagation(divRef.current);
    }
  });

  return (
    <div ref={divRef} className={position && ControlClasses[position]}>
      <div className={"leaflet-control"}>{children}</div>
    </div>
  );
};

export default LeafletControl;

  • Related