React-leaflet+Geoman加载API GeoJSON多边形,点击编辑顶点消失
解决react-leaflet + Geoman多边形顶点消失问题
问题根源
- 用index作为组件key:多边形数组变化时,React会复用组件实例,新旧多边形状态混淆,引发顶点丢失。
- 重复创建多边形实例:
useEffect依赖coordinates,每次坐标更新都会新建多边形,旧实例清理不彻底,编辑状态未同步,导致顶点显示异常。 - 重复绑定点击事件:每次
useEffect执行都绑定一次点击事件,多次点击会触发多次toggleEdit,造成编辑状态混乱。
修复步骤
1. 替换key为唯一标识
放弃使用数组index作为组件key,改用多边形的唯一ID(比如后端返回的id字段),确保每个Polygon组件实例稳定:
{polygons.map((polygon) => ( <Polygon key={polygon.id} coordinates={polygon.geometry.coordinates} onUpdate={(newCoords) => handlePolygonUpdate(polygon.id, newCoords)} /> ))}
2. 稳定多边形实例,避免重复创建
用useRef保存多边形实例,仅在首次渲染时创建,后续坐标更新时直接修改现有实例的坐标,并同步编辑状态:
function Polygon({ coordinates, onUpdate }) { const context = useLeafletContext(); const polygonRef = useRef(null); useEffect(() => { // 首次渲染创建多边形 if (!polygonRef.current) { polygonRef.current = L.polygon(coordinates).addTo(context.map); // 仅绑定一次点击事件 polygonRef.current.on('click', () => { polygonRef.current.pm.toggleEdit({ snappable: true, draggable: true, }); }); // 监听编辑完成事件,同步坐标到父组件 polygonRef.current.on('pm:edit', (e) => { onUpdate?.(e.layer.getLatLngs()); }); } else { // 坐标更新时,先保存编辑状态,更新后恢复 const isEditing = polygonRef.current.pm.enabled(); if (isEditing) polygonRef.current.pm.toggleEdit(); polygonRef.current.setLatLngs(coordinates); if (isEditing) polygonRef.current.pm.toggleEdit({ snappable: true, draggable: true }); } // 清理:移除多边形并关闭编辑 return () => { if (polygonRef.current) { if (polygonRef.current.pm?.enabled()) polygonRef.current.pm.toggleEdit(); context.map.removeLayer(polygonRef.current); polygonRef.current = null; } }; }, [coordinates, context.map, onUpdate]); return null; }
3. 同步编辑后的坐标到父组件
在父组件实现坐标更新逻辑,确保组件接收的coordinates与地图上的多边形状态一致:
function Map() { const [polygons, setPolygons] = useState([]); // 存储从API获取的多边形数据 const handlePolygonUpdate = (polygonId, newCoords) => { setPolygons(prev => prev.map(p => p.id === polygonId ? {...p, geometry: {...p.geometry, coordinates: newCoords}} : p )); }; return ( <MapContainer id="mapId" doubleClickZoom={false} style={{ height: "100vh" }} center={[-23.1573924, -45.7908461]} zoom={18} scrollWheelZoom={true} > {polygons.map((polygon) => ( <Polygon key={polygon.id} coordinates={polygon.geometry.coordinates} onUpdate={(newCoords) => handlePolygonUpdate(polygon.id, newCoords)} /> ))} </MapContainer> ); }
额外注意事项
- 确保Geoman与react-leaflet版本兼容,避免版本冲突引发异常。
- 清理函数必须移除地图上的多边形图层,防止旧图层残留导致交互混乱。
内容的提问来源于stack exchange,提问作者talison Brendon
相关产品推荐
相关产品推荐

