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

NextJS中SWR抛出错误后无法被处理的问题排查

处理SWR Fetcher错误时出现未处理运行时错误

我在处理SWR fetcher抛出的错误时遇到问题:每当fetcher内部抛出错误,应用就直接停止运行,显示“Unhandled Runtime Error”(未处理运行时错误)。

我的fetcher代码如下:

async function getFetcher(xxx: xxx) {
    const config = {
        params: {
            xxx: xxx
        },
    };

    return axios
        .get(url, config)
        .then((response) => {
            if (response.data.error) {
                console.log("Error");
                throw new Error(response.data.error);
            } else {
                const data = response.data;
                return data;
            }
        })
        .catch((error) => {
            console.log("throwing error...");
            throw error;
        });
}

export function useFetcher(xxx: xxx) {
    const { data, error } = useSWR("/integrations/facebook/getAdSpend", (url: string) => getFetcher(xxx), {
        suspense: true,
    });

    if (error) {
        // 永远不会执行到这里
        console.log("Error: " + error);
    }

    return {
        data: data,
        isLoadingData: !error && !data,
        error: error,
    };
}

我原本期望SWR能捕获错误并赋值给error变量,但应用直接崩溃,根本进不去检查error的逻辑分支。

错误截图:
未处理运行时错误截图

我尝试过在fetcher调用后直接加catch,但错误还是没传递到error变量:

export function useFetcher(xxx: xxx) {
    const { data, error } = useSWR("/integrations/facebook/getAdSpend", (url: string) => getFetcher(xxx).catch((error) => {
        // 能打印then块抛出的错误,但error变量还是空
        console.log(error);
    }), {
        suspense: true,
    });

    if (error) {
        // 依旧不会执行到这里
        console.log("Error: " + error);
    }

    return {
        data: data,
        isLoadingData: !error && !data,
        error: error,
    };
}

问题原因

核心问题是SWR在suspense: true模式下的错误处理逻辑和普通模式完全不同:
当启用Suspense时,SWR会直接抛出错误(遵循React Suspense的设计规范),而不是将错误赋值给返回的error变量。这就是为什么你的if (error)分支永远不会执行——错误已经冒泡到上层,未被捕获导致应用崩溃。

解决方案

方案1:关闭Suspense模式(最简单)

如果你不需要Suspense的特性,直接把suspense: true从配置中移除,SWR就会把错误正常赋值给error变量,你可以在hook里直接处理:

export function useFetcher(xxx: xxx) {
    const { data, error } = useSWR("/integrations/facebook/getAdSpend", () => getFetcher(xxx));

    if (error) {
        console.log("Error: " + error);
    }

    return {
        data,
        isLoadingData: !error && !data,
        error,
    };
}

方案2:保留Suspense,用React Error Boundary捕获错误

如果必须使用Suspense,你需要用React的Error Boundary组件来捕获抛出的错误(这是React Suspense处理错误的标准方式)。

首先创建一个Error Boundary组件:

import React from 'react';

class ErrorBoundary extends React.Component<
  { children: React.ReactNode; fallback: React.ReactNode },
  { hasError: boolean; error: Error | null }
> {
  constructor(props: { children: React.ReactNode; fallback: React.ReactNode }) {
    super(props);
    this.state = { hasError: false, error: null };
  }

  static getDerivedStateFromError(error: Error) {
    return { hasError: true, error };
  }

  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
    console.error("Error Boundary捕获到错误:", error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      return this.props.fallback;
    }
    return this.props.children;
  }
}

然后在使用useFetcher的组件外层用Error Boundary包裹:

<ErrorBoundary fallback={<div>加载数据失败,请稍后重试</div>}>
  <YourComponent /> {/* 这个组件内部调用了useFetcher */}
</ErrorBoundary>

额外优化:简化Fetcher代码

你的getFetcher里的.catch块只是重新抛出错误,完全可以去掉(axios的错误和你手动抛出的错误会自动向上传递),简化后代码更清晰:

async function getFetcher(xxx: xxx) {
    const config = {
        params: { xxx },
    };

    const response = await axios.get(url, config);
    if (response.data.error) {
        throw new Error(response.data.error);
    }
    return response.data;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 14:40:39