You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

同时执行异步函数导致React状态变量未正确更新

问题描述

我在使用ReactJS开发时,尝试将Google Maps Directions API返回的折线存储到状态变量中,以便后续条件渲染。定义的状态变量如下:

const [routes, setRoutes] = useState({1: null, 2: null})

问题在于,通过异步函数更新该状态时,仅第二个字段成功更新。函数调用代码如下:

useEffect(() => {
    drawTaxiRoute(0, data.taxis, data.origin, data.map, setRoutes, routes)
    drawTaxiRoute(1, data.taxis, data.origin, data.map, setRoutes, routes)
}, [])

异步函数(因回调仍保持异步特性,已移除async关键字)代码如下:

function drawTaxiRoute(N = 0, taxis, destination, map, setRoutes, routes) {
    ...

    directionsService.route(
        {
            origin: taxis[N].getPosition(),
            destination: destination,
            travelMode: google.maps.TravelMode.DRIVING,
        },
        function (result, status) {
            if (status == 'OK') {
                taxiRouteDisplay.setMap(map)
                setRoutes({...routes, [N+1]: result})
                console.log(result, N+1)
            }
        }
    )
}

控制台显示API已正确返回折线结果,但routes状态仅更新第二个字段,变为{1: null, 2: {...}}。怀疑是由于异步函数并行执行,第一次更新后的状态未及时同步,导致第二次调用时仍使用初始状态并覆盖了之前的更新。请问如何正确将两条折线都存入routes状态中?

解决方案

你的判断完全正确,问题核心是状态更新依赖了闭包捕获的旧状态值。

drawTaxiRoute在useEffect回调中执行时,传入的routes是组件初始渲染时的状态({1: null, 2: null})。两个API的异步回调执行时,都会基于这个旧状态做对象展开,第二次更新会直接覆盖第一次的结果。

推荐两种解决方法:

方法一:使用函数式状态更新

React的setState支持传入函数,该函数会接收当前最新的状态作为参数,返回新状态。这种方式能保证每次更新都基于最新状态,不受闭包旧值影响。

修改代码如下:

// 调用时移除routes参数
useEffect(() => {
    drawTaxiRoute(0, data.taxis, data.origin, data.map, setRoutes)
    drawTaxiRoute(1, data.taxis, data.origin, data.map, setRoutes)
}, [])

// 函数定义移除routes参数,改用函数式更新
function drawTaxiRoute(N = 0, taxis, destination, map, setRoutes) {
    ...

    directionsService.route(
        {
            origin: taxis[N].getPosition(),
            destination: destination,
            travelMode: google.maps.TravelMode.DRIVING,
        },
        function (result, status) {
            if (status == 'OK') {
                taxiRouteDisplay.setMap(map)
                setRoutes(prevRoutes => ({...prevRoutes, [N+1]: result}))
                console.log(result, N+1)
            }
        }
    )
}

方法二:用useRef同步最新状态

如果需要在其他场景也访问最新状态,可以用useRef来同步状态的实时值:

const [routes, setRoutes] = useState({1: null, 2: null})
const routesRef = useRef(routes)

// 状态更新时同步ref的值
useEffect(() => {
    routesRef.current = routes
}, [routes])

// 调用时传入routesRef
useEffect(() => {
    drawTaxiRoute(0, data.taxis, data.origin, data.map, setRoutes, routesRef)
    drawTaxiRoute(1, data.taxis, data.origin, data.map, setRoutes, routesRef)
}, [])

// 函数中使用ref的current属性获取最新状态
function drawTaxiRoute(N = 0, taxis, destination, map, setRoutes, routesRef) {
    ...

    directionsService.route(
        {
            origin: taxis[N].getPosition(),
            destination: destination,
            travelMode: google.maps.TravelMode.DRIVING,
        },
        function (result, status) {
            if (status == 'OK') {
                taxiRouteDisplay.setMap(map)
                setRoutes({...routesRef.current, [N+1]: result})
                console.log(result, N+1)
            }
        }
    )
}

优先推荐第一种方法,这是React官方针对异步状态更新的标准解决方案,代码更简洁且符合React状态管理逻辑。

内容的提问来源于stack exchange,提问作者jingwen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 06:32:06