React+TypeScript开发中markers变量在部分函数中读取为空数组问题求助
问题根因
- React函数组件每次触发重渲染(比如调用
set*系列的state修改方法)时,组件函数会完整重新执行一遍,你在组件顶层声明的var markers = []、var firstMarker每次都会被重置为初始值。只有本次渲染周期内刚修改后立刻调用的drawRoute能读到临时值,组件重渲染后其他所有时机读到的都是新初始化的空数组/undefined。 - 你额外维护一套与
markerState完全同源的markers变量属于冗余操作,很容易出现两边数据不同步的问题,比如你的undo操作只修改了markerState没有同步改markers,数据一致性完全无法保障。
修复方案
第一步:用useRef存储需要跨渲染周期持久化的变量
把你声明的两个普通可变变量替换为useRef,useRef的current属性不会随组件重渲染被重置,能稳定保存数据:
// 替换原来的var markers = []; var firstMarker const markersRef = useRef<GoogleLatLng[]>([]) const firstMarkerRef = useRef<google.maps.Marker | null>(null)
后续所有操作标记点数组的地方都用markersRef.current,操作第一个标记点的地方用firstMarkerRef.current即可。
第二步:删除冗余的markers维护逻辑,直接复用markerState
你已经有了官方的state变量markerState存储标记点,完全不需要额外维护一份独立的数组,直接把路线绘制和markerState绑定即可:
- 放开你注释掉的
useEffect(drawRoute,[markerState]),所有标记点变化的时候自动触发重绘,不需要在各个操作点手动调用drawRoute drawRoute里所有读取标记点的地方直接读markerState即可,不需要再维护另一份重复数组- 修正
undo逻辑:你原来写的slice(0,-2)是错误的,要撤销最后一个点应该是slice(0,-1),不需要单独操作标记点数组,直接改markerState就会自动触发重绘
第三步:补充细节优化
- 你现在给
directionsService、directionsDisplay用state存储,实际上这两个实例不需要触发重渲染,也可以换成useRef存储,性能更好 - 地图点击事件监听要加清理函数,避免重复绑定和内存泄漏:
useEffect(()=>{ if (!map) return const listener = google.maps.event.addListener(map, 'click', function(e) { setMarkerState(prev=>[...prev, e.latLng]) }) // 组件卸载/依赖更新时清理监听 return () => google.maps.event.removeListener(listener) }, [map])
修复后核心逻辑示例
// 直接基于markerState绘制路线,不需要额外维护标记点数组 const drawRoute = () => { console.log("当前标记点:", markerState) if (!map || !directionsDisplay || !directionsService) return; directionsDisplay.setMap(null) directionsDisplay.setOptions({ suppressMarkers: false, preserveViewport: true }) // 无标记点逻辑 if (markerState.length === 0 && firstMarkerRef.current) { firstMarkerRef.current.setMap(null) return } // 单个标记点逻辑 if (markerState.length === 1) { if (!firstMarkerRef.current) { firstMarkerRef.current = new google.maps.Marker({ position: markerState[0], map: map }) } else { firstMarkerRef.current.setPosition(markerState[0]) firstMarkerRef.current.setMap(map) } return } // 两点及以上绘制路线逻辑 firstMarkerRef.current?.setMap(null) const waypnts = markerState.slice(1, -1).map(point => ({ location: point, stopover: false })) directionsService.route({ origin: markerState[0], destination: markerState.at(-1)!, waypoints: waypnts, travelMode: google.maps.TravelMode.DRIVING }, (response, status) => { if (status === google.maps.DirectionsStatus.OK && map) { directionsDisplay.setMap(map) directionsDisplay.setDirections(response) // 计算路线长度 const length = response.routes[0].legs.reduce((sum, leg) => sum + leg.distance.value, 0) setRouteLength(length * 0.000621371) } else { alert(waypnts.length > 23 ? "途经点已达上限" : "路线请求失败") setMarkerState(prev => prev.slice(0,-1)) } }) } // markerState变化自动触发重绘 useEffect(drawRoute, [markerState, map, directionsService, directionsDisplay]) const undo = () => { setMarkerState(prev => prev.slice(0, -1)) } const clear = () => { setMarkerState([]) firstMarkerRef.current?.setMap(null) directionsDisplay?.setMap(null) }
内容的提问来源于stack exchange,提问作者Forrest Daggett
相关产品推荐
相关产品推荐

