同时执行异步函数导致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
相关产品推荐
相关产品推荐

