You need to enable JavaScript to run this app. React+TypeScript开发中markers变量在部分函数中读取为空数组问题求助
优惠活动
大模型
产品
解决方案
定价
更多

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绑定即可:

  1. 放开你注释掉的useEffect(drawRoute,[markerState]),所有标记点变化的时候自动触发重绘,不需要在各个操作点手动调用drawRoute
  2. drawRoute里所有读取标记点的地方直接读markerState即可,不需要再维护另一份重复数组
  3. 修正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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.12 00:15:08