Skip to content
Advertisement

Leaflet geoman “Button with this name already exists” error when creating a new button in react

I am trying to create a custom button for arrows in the drawing tool of leaflet-geoman. The idea was to work with the copyDrawControl function, and to use Line as a model to make Polylines with arrow tips.

I wrote a code mostly inspired from this demonstration https://codesandbox.io/s/394eq?file=/src/index.js and modified it for my goals. Here is the code :

import { useEffect } from "react";
import { useLeafletContext } from "@react-leaflet/core";
import "@geoman-io/leaflet-geoman-free";
import "@geoman-io/leaflet-geoman-free/dist/leaflet-geoman.css";

const Geoman = () => {
    const context = useLeafletContext();


    useEffect(() => {
        const leafletContainer = context.layerContainer || context.map;

        leafletContainer.pm.setGlobalOptions({ pmIgnore: false });


        //draw control options
        leafletContainer.pm.addControls({
            positions: {
                draw: 'topleft',
                edit: 'topright',
            },
            drawMarker: false,
            rotateMode: false,
            cutPolygon: false,
            position: "bottomright"
        });

        //new button
        leafletContainer.pm.Toolbar.copyDrawControl('Line', {
            name: 'SoonToBeArrow',
            block: 'draw',
            title: 'Display text on hover button',
            actions: [
                // uses the default 'cancel' action
                'cancel',
            ],
        });

        return () => {
            leafletContainer.pm.removeControls();
            leafletContainer.pm.setGlobalOptions({ pmIgnore: true });
        };
    }, [context]);

    return null;
};

export default Geoman;

When trying to add the copyDrawControl, I faced a bug that would announce that “Button with this name already exists”

I suspect its because I add the button inside a useEffect that gets called several times, but it’s also the only way to access leafletContainer, since it must be updated everytime the context changes. I tried creating another useEffect that contains the same context and my new button, but it did not work.

Does anyone have any suggestion on how to solve this ?

Thnak you in advance

Advertisement

Answer

You only want to run this effect once, just after context becomes available. In order to do this, we can make a state variable to track whether or not you’ve already added the control:

const Geoman = () => {
    const context = useLeafletContext();

    const [added, setAdded] = useState(false);

    useEffect(() => {
        const leafletContainer = context.layerContainer || context.map;

        // if the context is ready, and we've not yet added the control
        if (leafletContainer && !added){
          leafletContainer.pm.setGlobalOptions({ pmIgnore: false });

          //draw control options
          leafletContainer.pm.addControls({
            // ...
          });

          //new button
          leafletContainer.pm.Toolbar.copyDrawControl('Line', {
            // ...
          });

          // register that we've already added the control
          setAdded(true);
        }

        return () => {
          leafletContainer.pm.removeControls();
          leafletContainer.pm.setGlobalOptions({ pmIgnore: true });
        };
    }, [context]);

    return null;
};

In this way, you effect will run whenever context changes – once context is ready, you add the control. You register that you’ve added the control, but then your if statement will make sure that further changes in context will not try to keep adding controls again and again.

User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement