react-leaflet环境下如何更新leaflet-contextmenu菜单项的回调函数?
我太懂你这个坑了!之前用leaflet-contextmenu的时候也踩过类似的闭包陷阱——当传入的color属性更新后,菜单里的回调函数还抱着初始的旧值不放,完全不理会新的props,简直头疼。
咱们来拆解一下问题根源:
- 你的原始代码每次组件渲染都会重新创建Leaflet地图实例,这不仅浪费性能,还会导致菜单重复初始化;
- 更关键的是,初始创建菜单时,
showColor函数通过闭包捕获了当时的color值,后续color更新后,旧的回调依然引用着那个旧的闭包,而且Leaflet的上下文菜单不会自动感知React的props变化去更新自身。
下面是具体的修复方案,我会一步步把代码改好:
第一步:先把地图实例存起来,避免重复创建
首先用useRef来保存地图实例,确保组件挂载时只创建一次地图,不然每次color变化组件重新渲染,都会生成新的地图,这完全没必要:
import { useEffect, useRef, useCallback } from 'react'; import L from 'leaflet'; import 'leaflet-contextmenu'; import 'leaflet/dist/leaflet.css'; // 别忘了加Leaflet的样式,不然地图会显示不正常 const MapComponent = ({ color }) => { const mapRef = useRef(null); // 用ref存地图实例
第二步:让回调函数能捕获最新的color
用useCallback包裹showColor函数,这样只有当color变化时,这个函数才会重新创建,确保它永远引用最新的color值:
// 用useCallback包裹,确保color变了才更新函数 const showColor = useCallback(() => { alert(color); }, [color]);
第三步:初始化地图,监听color变化更新菜单
然后在useEffect里初始化地图,再单独加一个useEffect监听color的变化,一旦color更新,就调用leaflet-contextmenu的updateContextMenuItems方法刷新菜单的回调:
// 只在组件首次挂载时创建地图 useEffect(() => { if (!mapRef.current) { mapRef.current = L.map('map', { contextmenu: true, contextmenuWidth: 140, contextmenuItems: [ { text: 'Show color', callback: showColor, }, ], }); // 这里加个默认的瓦片图层,不然地图一片空白 L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap contributors' }).addTo(mapRef.current); } }, [showColor]); // 首次创建时用最新的showColor函数 // 监听color变化,更新上下文菜单 useEffect(() => { if (mapRef.current) { mapRef.current.updateContextMenuItems([ { text: 'Show color', callback: showColor, }, ]); } }, [color, showColor]); // color或showColor变化时就更新菜单
最后别忘了返回地图容器:
return <div id="map" style={{ height: '100vh', width: '100%' }} />; }; export default MapComponent;
为啥这样就好使了?
useRef保住了地图实例,不会每次渲染都重建,避免了不必要的性能损耗;useCallback让showColor函数只有在color变化时才重生,保证它永远能拿到最新的color;- 监听
color的useEffect会在每次color更新时,主动刷新上下文菜单的回调,把新的showColor绑定上去,这样右键点击时就会弹出最新的颜色值啦!
内容的提问来源于stack exchange,提问作者RouteMapper
相关产品推荐
相关产品推荐

