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.