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
相关产品推荐
相关产品推荐

