React中GraphQL+Axios添加数据后useEffect重取致应用崩溃
看起来你碰到的是请求配置、超时处理和逻辑漏洞叠加导致的问题,我之前也遇到过类似的情况,咱们一步步来排查和解决:
核心问题拆解
初始加载能正常获取数据,但添加事件后的二次请求超时失败,本质上是三个问题:请求权限不匹配、Axios无超时配置导致长时间阻塞、错误处理不完整引发应用崩溃,再加上useEffect里的异步状态逻辑有漏洞。
1. 先补全请求头的权限信息
你会发现fetchEvents的请求只带了Content-Type头,但handleConfirm里的mutation请求带了Authorization头。很大概率是你的后端在创建事件后,后续的查询请求也要求身份认证了——初始加载可能允许匿名访问,但添加事件后服务器的权限校验逻辑触发,不带token的查询请求被后端挂起(而不是直接拒绝),最终导致超时。
解决办法:把认证token也加到fetchEvents的请求头里:
const headers = { 'Content-Type': 'application/json', // 只有存在token时才添加认证头,兼容初始匿名加载的情况 ...(authContext.token && { 'Authorization': `Bearer ${authContext.token}` }) }
2. 给Axios设置合理超时时间
Axios默认的超时时间是0(也就是永不超时),这就是为什么你要等2分钟才触发catch的原因。这种长时间的请求阻塞会让应用处于假死状态,最终可能因为资源耗尽崩溃。
解决办法:在所有Axios请求里加上timeout配置,比如设置10秒超时:
axios.post('http://localhost:5000/graphql', body, { headers, timeout: 10000 // 10秒后自动终止请求 })
3. 修复useEffect里的异步状态逻辑漏洞
你当前的useEffect里,setAdded(false)和setInitialFetch(false)都是异步操作,直接在后面调用fetchEvents会导致状态更新和请求执行的顺序不可控,而且没有加载状态的管理,容易出现重复请求或者用户无感知的情况。
优化后的useEffect代码:
const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); useEffect(() => { const fetchEvents = async () => { setIsLoading(true); setError(null); // 每次请求前清空错误状态 try { const headers = { 'Content-Type': 'application/json', ...(authContext.token && { 'Authorization': `Bearer ${authContext.token}` }) }; const requestBody = { query: ` query { events { _id title description price } } ` }; console.log('fetching events from db'); const res = await axios.post( 'http://localhost:5000/graphql', JSON.stringify(requestBody), { headers, timeout: 10000 } ); setEvents(res.data.data.events); } catch (err) { console.error('Failed to fetch events:', err); setError('加载事件失败,请稍后重试'); // 设置错误状态,在UI上提示用户 } finally { setIsLoading(false); // 不管成功失败都结束加载状态 } }; // 初始加载或添加事件后触发请求 if (initialFetch || added) { // 重置触发状态 if (initialFetch) setInitialFetch(false); if (added) setAdded(false); fetchEvents(); } }, [added, initialFetch, authContext.token]); // 把token加入依赖,确保token变化时重新请求
这里用async/await让逻辑更清晰,添加了加载状态和错误状态,还补全了依赖项,避免因依赖缺失导致的bug。
4. 完善错误处理,避免应用崩溃
现在你的catch块只是打印错误,但没有在UI层面给用户反馈,也没有处理未捕获的异常。添加错误状态后,在UI里展示错误提示:
{error && <div className="alert alert-error">{error}</div>} {isLoading && <div>加载中...</div>}
这样用户能明确知道出了问题,应用也不会因为未处理的错误崩溃。
5. 排查后端是否存在请求阻塞
如果上面的方法都没用,那可能是后端的问题:比如创建事件后数据库锁死、GraphQL resolver逻辑卡住,或者服务器资源不足导致请求排队。可以去后端的日志里查看有没有慢查询、报错或者死锁的记录,比如检查MongoDB的查询是否添加了合适的索引,GraphQL的mutation和query resolver有没有逻辑漏洞。
内容的提问来源于stack exchange,提问作者user13730506

