react-leaflet-draw切换FeatureGroup时onEditStart/Stop事件累积触发问题
问题根因
react-leaflet-draw@0.19.0的EditControl组件底层直接在Leaflet地图实例上挂载全局事件监听器,当组件通过条件渲染隐藏时,内部没有实现事件监听器的自动解绑逻辑,导致历史激活过的EditControl实例的事件一直残留,触发时就会出现多个回调同时执行的问题。
可行解决方案
方案1:回调内增加激活状态判断(改造成本最低)
所有绑定给EditControl的onEditStart、onEditStop、onEdited等回调函数,第一行增加当前组是否为激活状态的判断,非激活状态直接中断逻辑执行,示例代码:const handleEditStart = (e) => { // 新增判断:当前组ID不等于激活ID直接返回 if (currentGroupId !== activeGroupID) return // 原有业务逻辑 }该方案不需要调整组件结构,也不会引入额外渲染副作用,适合快速修复问题。
方案2:统一使用单个全局EditControl(从根源避免多实例问题)
调整原有嵌套结构,把EditControl从每个MapContent的FeatureGroup中提取到Map组件下全局渲染,仅动态绑定当前激活的FeatureGroup实例即可:<Map> <MapContent id="foo" ref={fooGroupRef}> <FeatureGroup>{/* 图层内容 */}</FeatureGroup> </MapContent> <MapContent id="bar" ref={barGroupRef}> <FeatureGroup>{/* 图层内容 */}</FeatureGroup> </MapContent> {/* 全局仅一个EditControl,动态绑定当前激活的要素组 */} {activeGroupID && <EditControl featureGroup={getActiveGroupRef(activeGroupID).current} />} </Map>该方案彻底消除了多EditControl实例,不会出现事件累积问题,也不会影响图层正常展示。
方案3:非激活时手动销毁EditControl实例
若必须保留每个组独立的EditControl结构,可在MapContent组件内为EditControl绑定ref,在组件切换为非激活状态的副作用清理函数中,手动销毁控件实例并解绑事件:const MapContent = ({id, activeGroupID, children}) => { const editControlRef = useRef(null) const isActive = id === activeGroupID useEffect(() => { return () => { // 组件切换为非激活/卸载时,手动销毁控件实例 if (editControlRef.current) { const editInstance = editControlRef.current.leafletElement editInstance.remove() } } }, [isActive]) return ( <FeatureGroup> {children} {isActive && <EditControl ref={editControlRef} />} </FeatureGroup> ) }
不推荐方案说明
你之前尝试的添加随机key的方案会导致每次激活状态变更时,EditControl连同所属FeatureGroup完全卸载重绘,会引发图层闪跳、已绘制要素临时丢失等异常,不建议使用。
内容的提问来源于stack exchange,提问作者Superstooge

