react-leaflet中map已渲染却报null的弹窗打开异常问题
解决Leaflet地图Popup打开时map为null的报错问题
问题描述
设置了Popup的打开条件,当controlInfoWindow返回true时需要打开弹窗,但控制台显示map和marker的值均不为null的情况下,依然出现map为null的报错。
相关代码
控制弹窗显示的函数
const controlInfoWindow = () => { const marker = markerRef?.current; if (map != null && marker != null) { if (totalDeviceBtnClick == true) { return true; } else if (props.filter) { return true; } else if ( props.routeControl == 0 && props.areaFenceControl == 0 && props.showPopUpBox == false && props.ambulanceTraumaMedLatLng.length == 0 ) { if ( marker !== null && typeof marker == "object" && Object.keys(marker).length !== 0 ) { } return true; } else if ( props.routeControl == 1 && props.areaFenceControl == 0 && props.showPopUpBox == true ) { // marker?.openPopup(); return true; } else if ( props.routeControl == 0 && props.areaFenceControl == 1 && props.showPopUpBox == true ) { // debugger; return true; } else if ( props.routeControl == 1 && props.areaFenceControl == 1 && props.showPopUpBox == true ) { return true; } else { return false; } } };
触发弹窗的副作用
useEffect(() => { const marker = markerRef?.current; if (controlInfoWindow() == true && map != null) { if ( marker !== null && typeof marker == "object" && Object.keys(marker).length !== 0 ) { marker?.openPopup(); } } }, [map, markerRef.current, controlInfoWindow]);
Map状态同步逻辑
const [map, setMap] = useState(); const MyMap = ({ setMap }) => { const map = useMap(); useEffect(() => { setMap(map); }, []); return null; };
完整地图组件
<MapContainer style={{ height: "100%" }} center={path?.length == 0 && props?.areaFenceControl == 0 ? apiData.length > 0 ? { lat: mapData?.latitude, lng: mapData?.longitude } : { lat: 20.3341125, lng: 85.8012031 } : center } ref={mapRef} zoom={zoom} minZoom={parseInt(process.env.REACT_APP_MAP_MIN_ZOOM_LEVEL)} scrollWheelZoom={true} > <TileLayer url={osm.maptiler.url} attribution={osm.maptiler.attribution} /> <MyMap setMap={setMap} /> <MapEvents /> <Marker key={index} ref={ index == props.targetedVehicle ? markerRef : null } position={{lat: val.latitude, lng: val.longitude }} icon={OSMIcon} eventHandlers={{ click: () => popUpOpenBox(index) }} > <Popup closeButton={true}> A pretty CSS3 popup. <br /> Easily customizable. </Popup> </MapContainer>
问题分析
- Map状态同步不及时:
MyMap组件中的useEffect未依赖map,仅在初始化时同步map状态,后续map更新无法同步到父组件state。 - 闭包导致旧值引用:
controlInfoWindow未用useCallback包裹且无依赖声明,函数内部引用的map、props可能为旧值,即使当前控制台显示map非null,函数内仍可能引用之前的null值。 - 判断顺序不合理:
useEffect先调用controlInfoWindow()再判断map != null,若函数内引用的map是旧的null值,会直接触发报错。
解决方案
1. 修复Map状态同步逻辑
确保MyMap能实时同步map状态到父组件:
const MyMap = ({ setMap }) => { const map = useMap(); useEffect(() => { setMap(map); }, [map, setMap]); // 添加依赖,确保map更新时同步 return null; };
2. 优化控制函数,解决闭包问题
用useCallback包裹函数并声明所有依赖:
import { useCallback } from 'react'; const controlInfoWindow = useCallback(() => { const marker = markerRef?.current; if (!map || !marker) { return false; } if (totalDeviceBtnClick) { return true; } if (props.filter) { return true; } if ( props.routeControl === 0 && props.areaFenceControl === 0 && !props.showPopUpBox && props.ambulanceTraumaMedLatLng.length === 0 ) { return true; } if ( props.routeControl === 1 && props.areaFenceControl === 0 && props.showPopUpBox ) { return true; } if ( props.routeControl === 0 && props.areaFenceControl === 1 && props.showPopUpBox ) { return true; } if ( props.routeControl === 1 && props.areaFenceControl === 1 && props.showPopUpBox ) { return true; } return false; }, [ map, markerRef, totalDeviceBtnClick, props.filter, props.routeControl, props.areaFenceControl, props.showPopUpBox, props.ambulanceTraumaMedLatLng.length ]);
3. 调整副作用判断顺序
先验证map和marker有效性,再执行控制逻辑:
useEffect(() => { const marker = markerRef?.current; if (map && marker && controlInfoWindow()) { marker.openPopup(); } }, [map, markerRef.current, controlInfoWindow]);
4. 额外检查
确认markerRef仅绑定到目标车辆的Marker组件,避免ref为空;同时确保Marker与Popup已正确关联,调用marker.openPopup()即可触发弹窗。
内容的提问来源于stack exchange,提问作者shashi
相关产品推荐
相关产品推荐

