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

使用usePrefetchQuery预取数据时如何处理404及离线错误并兼容useSuspenseQuery

问题描述

使用usePrefetchInfiniteQuery预取无限列表数据时,遇到URL错误导致404、后端离线这类场景,应用会直接崩溃并抛出控制台错误。但根据文档描述:The prefetch functions never throws errors because they usually try to fetch again in a useQuery which is a nice graceful fallback.,预期预取出错时应该自动跳过,由后续useQuery优雅降级。希望在保留现有预取逻辑的前提下,解决应用崩溃问题,实现预期的降级处理。

现有代码实现

const initialPageParam = "api/v1/logs-list/json/?limit=25&offset=0";
const queryFn = async (pageParam: string, customKy: KyInstance) => {
  const link = pageParam.split("a1pi/v1/")[1];
  const resolvedLink = `${link}&resolved=false`;
  return await customKy.get<LogsResponse>(resolvedLink).json();
};

export const usePrefetchInfiniteLogsQuery = () => {
  const { customKy } = useKy();
  usePrefetchInfiniteQuery({
    queryKey: ["infiniteLogs", customKy],
    queryFn: async ({ pageParam }) => queryFn(pageParam, customKy),
    initialPageParam,
    getNextPageParam: (lastPage) => lastPage?.next,
    pages: 3,
  });
};

export const useInfiniteLogsQuery = () => {
  const { customKy } = useKy();
  return useSuspenseInfiniteQuery({
    queryKey: ["infiniteLogs", customKy],
    queryFn: async ({ pageParam }) => queryFn(pageParam, customKy),
    initialPageParam,
    getNextPageParam: (lastPage) => lastPage?.next,
  });
};

父组件使用方式

const SosFormWrapper = () => {
  usePrefetchSosQuery();

  return (
    <Suspense fallback={<Loader />}>
      <EmergencyModal />
      <SosForm />
    </Suspense>
  );
};

解决方案

1. 在queryFn内部主动捕获错误

预取崩溃的核心原因是queryFn抛出了未捕获的异常。可以在queryFn中直接捕获错误,返回带错误标记的结构而非抛出错误:

const queryFn = async (pageParam: string, customKy: KyInstance) => {
  try {
    const link = pageParam.split("a1pi/v1/")[1];
    const resolvedLink = `${link}&resolved=false`;
    return await customKy.get<LogsResponse>(resolvedLink).json();
  } catch (error) {
    // 捕获错误并返回带标记的结构,避免抛出异常
    console.warn("预取日志数据失败:", error);
    return { data: [], next: null, isError: true } as Partial<LogsResponse>;
  }
};

这样预取时即使出错,也不会导致应用崩溃,后续useSuspenseInfiniteQuery拿到数据后,可在组件中判断isError状态并做降级渲染。

2. 显式配置throwOnError: false

虽然预取函数默认不应抛出错误,但显式配置该参数可以强制抑制错误抛出:

export const usePrefetchInfiniteLogsQuery = () => {
  const { customKy } = useKy();
  usePrefetchInfiniteQuery({
    queryKey: ["infiniteLogs", customKy],
    queryFn: async ({ pageParam }) => queryFn(pageParam, customKy),
    initialPageParam,
    getNextPageParam: (lastPage) => lastPage?.next,
    pages: 3,
    throwOnError: false, // 强制关闭预取时的错误抛出
  });
};

3. 在useSuspenseInfiniteQuery中处理降级逻辑

结合预取返回的错误标记,在查询钩子中封装错误判断逻辑,方便组件使用:

export const useInfiniteLogsQuery = () => {
  const { customKy } = useKy();
  const result = useSuspenseInfiniteQuery({
    queryKey: ["infiniteLogs", customKy],
    queryFn: async ({ pageParam }) => queryFn(pageParam, customKy),
    initialPageParam,
    getNextPageParam: (lastPage) => lastPage?.next,
  });

  // 统一判断是否存在预取或查询错误
  const hasError = result.pages.some(page => page?.isError);
  
  return { ...result, hasError };
};

组件中使用时:

const SosForm = () => {
  const { data, hasError } = useInfiniteLogsQuery();
  
  if (hasError) {
    return <div>加载失败,请稍后重试</div>;
  }
  
  // 正常渲染列表逻辑
};

4. 全局错误边界兜底

添加React错误边界,捕获组件树中未处理的错误,避免整个应用崩溃:

class LogsErrorBoundary extends React.Component {
  state = { hasError: false };

  static getDerivedStateFromError() {
    return { hasError: true };
  }

  componentDidCatch(error, errorInfo) {
    console.error("日志模块错误:", error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      return <div>加载出现异常,请刷新页面重试</div>;
    }
    return this.props.children;
  }
}

然后包裹目标组件:

const SosFormWrapper = () => {
  usePrefetchSosQuery();

  return (
    <Suspense fallback={<Loader />}>
      <LogsErrorBoundary>
        <EmergencyModal />
        <SosForm />
      </LogsErrorBoundary>
    </Suspense>
  );
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 00:58:28