React自定义Http Hook中AbortController报错问题及优化咨询
问题解决:React自定义Http Hook中止请求导致后端报错的优化方案
问题背景
实现了带页面跳转请求中止功能的自定义React Http Hook,结合useEffect获取数据时,后端出现报错,但请求正常执行、功能符合预期。需要优化代码消除报错,并确认是否需要保留AbortController功能。
报错原因
当页面跳转导致组件卸载时,Hook里的AbortController会中止未完成的请求。此时后端已经查询到用户数据,但尝试通过res.json()返回响应时,请求连接已被前端中止,Node.js会抛出ECONNABORTED或类似的连接中断错误。
代码优化方案
1. 前端Hook:区分中止错误与业务错误
在sendRequest的catch块中,判断错误是否为请求中止导致的AbortError,这类错误不需要触发全局错误提示,避免干扰正常业务流程。
修改后的http-hook.ts:
import { useCallback, useRef, useEffect } from "react"; import { useSelector } from "react-redux"; import { useDispatch } from "react-redux"; import { selectError, showError } from "src/redux/error"; import { selectLoading, startLoading, stopLoading } from "src/redux/loading"; export const useHttpClient = () => { const dispatch = useDispatch(); const error = useSelector(selectError); const loading = useSelector(selectLoading); const activeHttpRequests: any = useRef([]); const sendRequest = useCallback( async (url, method = "GET", body = null, headers = {}) => { dispatch(startLoading()); const httpAbortCtrl = new AbortController(); activeHttpRequests.current.push(httpAbortCtrl); try { const response = await fetch(url, { method, body, headers, signal: httpAbortCtrl.signal, }); const responseData = await response.json(); activeHttpRequests.current = activeHttpRequests.current.filter( (reqCtrl) => reqCtrl !== httpAbortCtrl ); if (!response.ok) { throw new Error(responseData.message); } dispatch(stopLoading()); return responseData; } catch (err: any) { // 区分请求中止错误和业务错误 if (err.name !== "AbortError") { dispatch(showError(err.message || "请求失败")); } dispatch(stopLoading()); // 仅抛出非中止错误,避免组件中捕获到无意义的中止错误 if (err.name !== "AbortError") throw err; } }, [dispatch] // 修复依赖:dispatch需要加入useCallback依赖,否则可能导致旧的dispatch引用 ); useEffect(() => { return () => { activeHttpRequests.current.forEach((abortCtrl: any) => abortCtrl.abort()); }; }, []); return { loading, error, sendRequest }; };
关键修改点:
- 在catch块中判断
err.name === "AbortError",这类错误不触发全局错误提示,也不抛出给组件 - 修复
useCallback的依赖,加入dispatch(React中dispatch是稳定引用,不会导致不必要的重渲染)
2. 后端Controller:捕获连接中断错误
在后端处理响应时,捕获请求中止导致的错误,避免无意义的500错误日志。
修改后的getCurrentUser.ts:
const getCurrentUser = async ( req: express.Request, res: express.Response, next: express.NextFunction ) => { const userId = req.params.userId; let user; try { user = await User.findById(userId); } catch (err) { const error = new HttpError("Could not fetch user", 500); return next(error); } try { res.json({ user: user.toObject({ getters: true }) }); } catch (err: any) { // 捕获请求中止导致的响应错误,不返回500 if (err.code === "ECONNABORTED") { console.log("请求已被前端中止,无需处理响应"); return; } const error = new HttpError("Failed to send response", 500); return next(error); } };
关键修改点:
- 把
res.json()包裹在try-catch中,捕获ECONNABORTED错误,这类情况属于正常的请求中止,无需返回错误
是否需要保留AbortController功能?
建议保留,原因如下:
- 减少服务器负载:中止未完成的请求,避免服务器继续处理已经不需要的请求
- 避免内存泄漏:组件卸载后,避免请求完成后调用
setState导致的React警告 - 提升用户体验:快速切换页面时,中止旧请求,避免旧数据覆盖新页面的正确数据
内容的提问来源于stack exchange,提问作者Vladislav Marinov
相关产品推荐
相关产品推荐

