React多async/await调用报RangeError: Maximum call stack size exceeded
RangeError: Maximum call stack size exceeded 问题修复
报错根因
这个调用栈溢出和异步函数调用数量无关,核心是函数名遮蔽触发无限递归,同时代码存在React状态时序错误、直接修改状态的逻辑问题:
- 自定义请求入口函数命名为
fetch,和浏览器全局fetchAPI重名,产生作用域遮蔽:fetchRides、fetchUser内部调用fetch发请求时,实际拿到的是当前作用域下自定义的fetch函数,导致执行流无限递归调用自身,最终撑爆调用栈。 - React的
setState是异步批处理逻辑,接口请求完成后立即读取updatedRides、user状态,拿到的是渲染闭包里的初始旧值,距离计算逻辑拿不到正确的接口返回数据。 - 直接给状态里的
ride对象挂载distance属性,违反React状态不可变原则,会引发渲染不更新、状态不一致问题。
修复后完整代码
import { useState, useEffect } from "react"; import Navbar from "./Navbar"; const Rides = () => { const [rides, setRides] = useState([]); const [user, setUser] = useState({}); const [updatedRides, setUpdatedRides] = useState([]); useEffect(() => { const fetchRides = async () => { const data = await fetch('https://assessment.api.vweb.app/rides'); return await data.json(); } const fetchUser = async () => { const data = await fetch('https://assessment.api.vweb.app/user'); return await data.json(); } const calculateDistance = (path, user_station) => { let min = Math.abs(user_station - path[0]); for(let i = 0; i<path.length; i++){ if(path[i] === user_station){ return 0; } if(Math.abs(path[i] - user_station) < min){ min = Math.abs(path[i] - user_station); } } return min; } // 重命名入口函数,避免和全局fetch重名导致作用域遮蔽 const loadInitialData = async() => { // 无依赖的接口用Promise.all并行拉取,缩短加载时间 const [ridesData, userData] = await Promise.all([ fetchRides(), fetchUser() ]); // 直接基于接口返回值计算距离,不依赖中间状态更新,规避闭包旧值问题 const ridesWithDistance = ridesData.map((ride) => ({ ...ride, distance: calculateDistance(ride.station_path, userData.station_code) })); // 批量更新状态 setRides(ridesData); setUser(userData); setUpdatedRides(ridesWithDistance); } loadInitialData().catch((e) => { console.log(e); }) }, []) return( <div> <Navbar user={user}/> <div className="rides"> {updatedRides.map((ride) => ( <div className="rideDetail" key={ride.id}> <img src={ride.map_url} alt="Ride_map" /> <div> <p>Ride Id : {ride.id}</p> <p>Origin Station : {ride.origin_station_code}</p> <p>Station Path : {ride.station_path.join(', ')}</p> <p>Date : {ride.date}</p> <p>Distance : {ride.distance}</p> </div> </div> ))} </div> </div> ) } export default Rides;
修复说明
- 把原自定义的
fetch入口函数重命名为loadInitialData,彻底解决函数名遮蔽导致的无限递归问题,从根源消除调用栈溢出报错。 - 调整数据流转逻辑:接口返回数据后直接计算距离,不依赖React状态更新后的取值,避免拿到旧闭包值导致计算错误。
- 距离计算时通过展开运算符生成新的ride对象,不直接修改原始响应数据,符合React不可变数据要求。
- 补充React hooks的导入,给列表项添加唯一
key属性,对无依赖的接口请求改为并行拉取,优化加载性能。
内容的提问来源于stack exchange,提问作者Anuj kumar Jaimini
相关产品推荐
相关产品推荐

