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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 23:35:41