You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 10:30:41