React中创建Leaflet Geoman自定义按钮出现「按钮名称已存在」错误
解决Leaflet Geoman中copyDrawControl重复添加按钮报错的问题
问题原因
useEffect 依赖 context 触发多次执行,每次执行都会调用 copyDrawControl 创建同名按钮,而Leaflet Geoman不允许重复注册同名的绘图控件,因此抛出「Button with this name already exists」错误。
解决方案
方案1:添加前检查按钮是否已存在
在调用 copyDrawControl 前,先检查目标控件块(这里是draw)中是否已存在同名按钮,仅当不存在时才创建:
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 }); // 配置绘图控件 leafletContainer.pm.addControls({ positions: { draw: 'topleft', edit: 'topright', }, drawMarker: false, rotateMode: false, cutPolygon: false, position: "bottomright" }); // 检查自定义箭头按钮是否已存在 const drawControls = leafletContainer.pm.Toolbar.controls.draw || []; const hasArrowButton = drawControls.some(ctrl => ctrl.name === 'SoonToBeArrow'); if (!hasArrowButton) { // 创建自定义箭头按钮 leafletContainer.pm.Toolbar.copyDrawControl('Line', { name: 'SoonToBeArrow', block: 'draw', title: 'Display text on hover button', actions: ['cancel'], }); } return () => { leafletContainer.pm.removeControls(); leafletContainer.pm.setGlobalOptions({ pmIgnore: true }); }; }, [context]); return null; }; export default Geoman;
方案2:使用useRef标记初始化状态
通过useRef存储一个标记,记录按钮是否已添加,避免重复执行创建逻辑:
import { useEffect, useRef } 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(); const isArrowControlAdded = useRef(false); // 标记按钮是否已添加 useEffect(() => { const leafletContainer = context.layerContainer || context.map; leafletContainer.pm.setGlobalOptions({ pmIgnore: false }); // 配置绘图控件 leafletContainer.pm.addControls({ positions: { draw: 'topleft', edit: 'topright', }, drawMarker: false, rotateMode: false, cutPolygon: false, position: "bottomright" }); // 仅当未添加过按钮时执行创建逻辑 if (!isArrowControlAdded.current) { leafletContainer.pm.Toolbar.copyDrawControl('Line', { name: 'SoonToBeArrow', block: 'draw', title: 'Display text on hover button', actions: ['cancel'], }); isArrowControlAdded.current = true; } return () => { leafletContainer.pm.removeControls(); leafletContainer.pm.setGlobalOptions({ pmIgnore: true }); // 组件卸载时重置标记,下次挂载可重新添加 isArrowControlAdded.current = false; }; }, [context]); return null; }; export default Geoman;
方案3:先移除旧控件再创建
如果context变化意味着地图实例更换,可先尝试移除已存在的同名控件,再创建新的,确保不会重复:
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 }); // 配置绘图控件 leafletContainer.pm.addControls({ positions: { draw: 'topleft', edit: 'topright', }, drawMarker: false, rotateMode: false, cutPolygon: false, position: "bottomright" }); // 先尝试移除已存在的同名按钮(不存在则忽略错误) try { leafletContainer.pm.Toolbar.removeControl('SoonToBeArrow', 'draw'); } catch (err) {} // 创建自定义箭头按钮 leafletContainer.pm.Toolbar.copyDrawControl('Line', { name: 'SoonToBeArrow', block: 'draw', title: 'Display text on hover button', actions: ['cancel'], }); return () => { leafletContainer.pm.removeControls(); leafletContainer.pm.setGlobalOptions({ pmIgnore: true }); }; }, [context]); return null; }; export default Geoman;
内容的提问来源于stack exchange,提问作者Louri
相关产品推荐
相关产品推荐

