reactjsreact-hooksleafletreact-leafletleaflet-geoman

React useCallback function runs in infinite loop


I am trying to use leaflet-geoman library in a React project. I need to create a custom toolbar button that enables and disables global drag mode.

When toolbar button is clicked, map.pm.enableGlobalDragMode(); function enables global mode. When toolbar button is clicked again, map.pm.disableGlobalDragMode(); causes useCallback function afterClick running in infinite loop.

codesandbox.io

useDraw.js

import React from "react";

const useDraw = (mapRef) => {
  const afterClick = React.useCallback(() => {
    console.log("afterclick");
    const map = mapRef.current.leafletElement;
    let editDragEnabled = false;
    if (!editDragEnabled) {
      console.log("enable");
      map.pm.enableGlobalDragMode();
      editDragEnabled = true;
    } else {
      console.log("disable");
      map.pm.disableGlobalDragMode();
      editDragEnabled = false;
    }
  }, [mapRef]);

  React.useEffect(() => {
    const map = mapRef.current.leafletElement;

    var actions = ["finishMode"];
    map.pm.addControls({
      drawRectangle: false,
      drawMarker: false,
      drawPolyline: false,
      drawPolygon: false,
      drawCircle: false,
      drawCircleMarker: false,
      removalMode: false,
      editMode: false,
      cutPolygon: false,
      dragMode: false
    });
    map.pm.Toolbar.createCustomControl({
      name: "DragEdit",
      block: "custom",
      title: "Edit and Drag Layers",
      onClick: () => afterClick(),
      actions: actions,
      toggle: true
    });
  }, [mapRef, afterClick]);
};

export default useDraw;

Solution

  • The problem is, that when enableGlobalDragMode ( or disable) the Control of the original drag button is activated and this disables your custom button (because all other buttons are disabled, so that only one mode can be active).

    I suggest to use the code from the enableGlobalDragMode function instead of calling it, which cause a change on the controls:

    const afterClick = React.useCallback(() => {
        console.log("afterclick");
        const map = mapRef.current.leafletElement;
    
        const layers = L.PM.Utils.findLayers(map);
        let dragMode = map.pm._customModeEnabled || false;
        if(!dragMode){
          console.log("enable");
          layers.forEach((layer)=>{
            layer.pm.enableLayerDrag();
          })
        }else{
          console.log("disable");
          layers.forEach((layer)=>{
            layer.pm.disableLayerDrag();
          })
        }
        map.pm._customModeEnabled = !dragMode;
      }, [mapRef]);