React Google Maps API点击首个标记时map状态为null问题求助
求助:React Google Maps API 点击Marker首次不显示Circle问题
需求说明
在Google地图上显示一组Marker,点击标记时在地图上添加Google Circle。
实际现象
首次点击标记时,圆形不显示;但点击第二个及以后的标记时,圆形能正常显示。
问题排查
通过console.log追踪map状态发现,首次进入markerClicked函数时,map状态值为null,导致无法创建圆形;但地图首次加载时和后续点击标记时,map状态都包含有效的地图实例。
组件代码
import { GoogleMap, MarkerF, useJsApiLoader } from "@react-google-maps/api"; import Box from '@mui/material/Box'; import { useSelector } from 'react-redux' let mapCircle1 = null export default function MapPage() { // 标记数组存储在REDUX中 const selectMarkersArray = state => state.markersArray const markersArray = useSelector(selectMarkersArray) // 存储选中的标记(未选中时为null) const [selectedMarker, setSelectedMarker] = useState(null); // 谷歌地图配置项 let center = { lat: 43.3318, lng: 5.0550 } let zoom = 15 const containerStyle = { width: "100%", height: "100%" } // 谷歌地图加载逻辑 const { isLoaded } = useJsApiLoader({ id: 'google-map-script', googleMapsApiKey: "MY-GOOGLE-KEY" }) const [map, setMap] = useState(null) const onLoad = useCallback(function callback(map) { setMap(map) console.log('map value in onLoad :') console.log(map) }, []) const onUnmount = useCallback(function callback(map) { setMap(null) }, []) // 标记点击触发的函数 function markerClicked(props) { console.log('map value in markerClicked :') console.log(map) // 创建圆形配置 let circleOption1 = { fillColor: "#2b32ac ", map: map, center: {lat:props.marker.spotLatitude, lng:props.marker.spotLongitude}, radius: props.marker.spotCircleRadius, }; mapCircle1 = new window.google.maps.Circle(circleOption1); // 更新选中标记状态 setSelectedMarker({...props.marker}) } return (isLoaded ? ( <Box height="80vh" display="flex" flexDirection="column"> <GoogleMap mapContainerStyle={containerStyle} center={center} zoom={zoom} onLoad={onLoad} onUnmount={onUnmount} > {markersArray.map((marker, index) => { return ( <MarkerF key={index.toString()} position={{lat:marker.spotLatitude, lng:marker.spotLongitude}} onClick={() => markerClicked({marker:marker})} > </MarkerF> ) })} </GoogleMap> </Box> ) : <></> ) };
控制台日志
map value in onLoad : jj {gm_bindings_: {…}, __gm: hda, gm_accessors_: {…}, mapCapabilities: {…}, renderingType: 'UNINITIALIZED', …} map value in markerClicked : null map value in markerClicked : jj {gm_bindings_: {…}, __gm: hda, gm_accessors_: {…}, mapCapabilities: {…}, renderingType: 'RASTER', …}
问题原因
首次点击时markerClicked函数捕获的是组件初始渲染时的map状态(值为null),这是函数闭包特性导致的:组件首次渲染时创建的markerClicked函数,会绑定当时的map状态值(null)。虽然之后onLoad更新了map状态,但这个旧的markerClicked函数并没有重新创建,直到组件因setSelectedMarker触发重新渲染,才会生成新的markerClicked函数,此时它捕获的是更新后的有效map实例。
解决方案
方式1:用useCallback包裹markerClicked,依赖map状态
将markerClicked用useCallback包裹,并把map加入依赖数组,这样每次map状态更新时,都会生成新的函数实例,确保能获取到最新的map值:
const markerClicked = useCallback((props) => { console.log('map value in markerClicked :') console.log(map) if (!map) return; // 增加空值判断,避免报错 let circleOption1 = { fillColor: "#2b32ac ", map: map, center: {lat:props.marker.spotLatitude, lng:props.marker.spotLongitude}, radius: props.marker.spotCircleRadius, }; mapCircle1 = new window.google.maps.Circle(circleOption1); setSelectedMarker({...props.marker}) }, [map])
方式2:通过ref存储map实例
使用useRef替代useState存储map实例,因为ref的更新不会触发组件重新渲染,且能直接获取最新值,避免闭包问题:
// 替换原来的useState为useRef const mapRef = useRef(null) const onLoad = useCallback(function callback(map) { mapRef.current = map console.log('map value in onLoad :') console.log(map) }, []) const onUnmount = useCallback(function callback(map) { mapRef.current = null }, []) // 修改markerClicked函数 function markerClicked(props) { const map = mapRef.current console.log('map value in markerClicked :') console.log(map) if (!map) return; let circleOption1 = { fillColor: "#2b32ac ", map: map, center: {lat:props.marker.spotLatitude, lng:props.marker.spotLongitude}, radius: props.marker.spotCircleRadius, }; mapCircle1 = new window.google.maps.Circle(circleOption1); setSelectedMarker({...props.marker}) }
额外优化建议
全局变量mapCircle1可能导致内存泄漏,建议改用ref存储,在每次创建新Circle前先销毁旧的Circle:
// 用ref存储circle实例 const circleRef = useRef(null) // 在markerClicked里添加销毁逻辑 if (circleRef.current) { circleRef.current.setMap(null) } circleRef.current = new window.google.maps.Circle(circleOption1)
内容的提问来源于stack exchange,提问作者Bol
相关产品推荐
相关产品推荐

