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

Next.js自定义useFetch钩子错误状态更新异常求助

自定义useFetch钩子错误处理问题

我为Next.js应用编写了自定义useFetch钩子,代码如下:

import HTTPMethod from "http-method-enum";
import { useState } from "react";

export default function useFetch() {
    const [data, setData] = useState(null);
    const [error, setError] = useState("");
    const [loading, setLoading] = useState(false);

    const sendFetch = async (url: string, method: HTTPMethod, body: any): Promise<any> => {
        try {
            setLoading(true);
            const rawResponse = await fetch(url, {
                method,
                headers: {
                    Accept: "application/json",
                    "Content-Type": "application/json",
                },
                body: JSON.stringify({
                    body,
                }),
            });

            const response = await rawResponse.json();
            setData(response);
        } catch (error: any) {
            setError(error);
        } finally {
            setLoading(false);
        }
    };

    return [data, loading, error, sendFetch];
}

目前除错误处理外功能正常:当fetch返回400错误码时,Catch块不会触发,所有响应(无论是否为错误)都存入data状态,error状态始终为空。我需要将错误响应存入error状态,且请勿推荐Axios或其他fetch库(边缘环境运行缓慢)。


编辑:

我修改了代码:

import HTTPMethod from "http-method-enum";
import { useState } from "react";

export default function useFetch() {
    const [data, setData] = useState(null);
    const [error, setError] = useState("");
    const [loading, setLoading] = useState(false);

    console.log(error)

    const sendFetch = async (url: string, method: HTTPMethod, body: any): Promise<any> => {
        try {
            setLoading(true);
            const rawResponse = await fetch(url, {
                method,
                headers: {
                    Accept: "application/json",
                    "Content-Type": "application/json",
                },
                body: JSON.stringify({
                    body,
                }),
            });

            const response = await rawResponse.json();

            if (!response.ok) {
                throw new Error(response.message)
            }
            setData(response);
        } catch (error: any) {
            setError(error);
        } finally {
            setLoading(false);
        }
    };

    return [data, loading, error, sendFetch];
}

但状态仍未更新:直接传入字符串setError("Error")时能更新状态,但传入catch里的error参数时,error状态还是为空。


解决方案

核心问题分析

  1. fetch不触发catch的原因:fetch仅在网络层面失败(如断网、域名不存在)时才会抛出错误,HTTP错误码(4xx/5xx)不会自动触发catch。你之前错误检查了解析后的JSON对象的ok属性,而非原始响应对象的rawResponse.ok(状态码200-299时为true)。
  2. setError传入error参数不更新的原因:直接存入Error对象可能导致React浅比较无法识别状态变化;另外如果后端返回的错误响应没有message字段,抛出的Error会是空消息,看起来像状态未更新。

修正后的代码

import HTTPMethod from "http-method-enum";
import { useState } from "react";

export default function useFetch() {
    const [data, setData] = useState<null | any>(null);
    const [error, setError] = useState<string>("");
    const [loading, setLoading] = useState(false);

    const sendFetch = async (url: string, method: HTTPMethod, body: any): Promise<void> => {
        try {
            setLoading(true);
            // 重置旧状态,避免干扰新请求
            setError("");
            setData(null);

            const rawResponse = await fetch(url, {
                method,
                headers: {
                    Accept: "application/json",
                    "Content-Type": "application/json",
                },
                body: body ? JSON.stringify(body) : undefined, // 移除多余的{body}包裹,匹配后端参数格式
            });

            // 检查原始响应的状态
            if (!rawResponse.ok) {
                let errorMessage = `请求失败:状态码 ${rawResponse.status}`;
                // 兼容不同格式的错误响应
                try {
                    const errorResp = await rawResponse.json();
                    errorMessage = errorResp.message || errorMessage;
                } catch (e) {
                    errorMessage = await rawResponse.text() || errorMessage;
                }
                throw new Error(errorMessage);
            }

            const responseData = await rawResponse.json();
            setData(responseData);
        } catch (err) {
            // 将Error对象转为字符串存入状态,确保React能识别更新
            setError(err instanceof Error ? err.message : String(err));
        } finally {
            setLoading(false);
        }
    };

    return [data, loading, error, sendFetch] as const;
}

关键修复点

  • 检查rawResponse.ok而非解析后的JSON对象,确保HTTP错误码能进入catch块
  • 兼容不同格式的错误响应(JSON/纯文本),避免解析失败丢失错误信息
  • 请求前重置错误和数据状态,避免旧状态残留
  • 将Error对象转为字符串存入error状态,确保状态更新能被React正确识别
  • 修正body传递格式,移除多余的{body}包裹,匹配后端预期参数结构

内容的提问来源于stack exchange,提问作者Ethan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 02:06:02