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

React-Query Suspense模式未显示加载状态的问题排查与修复

问题描述

我正在开发React项目,尝试使用@tanstack/react-query库结合Suspense处理加载状态。但获取图片时,加载回退组件并未显示。

Loading doesnt appear while fetching

应用简化代码

App组件

import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import ImageList from "./components/suspense/ImageList";

const queryClient = new QueryClient();

const App = () => {
  return (
    <QueryClientProvider client={queryClient}>
      <ImageList />
    </QueryClientProvider>
  );
};

export default App;

获取函数与自定义Hook

import { useQuery } from "@tanstack/react-query";

export interface PicsumImage {
  id: string;
  author: string;
  download_url: string;
}

const fetchPicsumImages = async (): Promise<PicsumImage[]> => {
  const response = await fetch("https://picsum.photos/v2/list?page=1&limit=5");
  if (!response.ok) {
    throw new Error("Network response was not ok");
  }
  return response.json();
};

const usePicsumImages = () => {
  return useQuery<PicsumImage[]>({
    queryKey: ["picsumImages"],
    queryFn: fetchPicsumImages,
  });
};

export default usePicsumImages;

ImageList组件

import usePicsumImages from "./usePicsumImages";

const ImageList = () => {
  const { data } = usePicsumImages();

  return (
    <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
      {data?.map((image) => (
        <div key={image.id} className="rounded shadow-md overflow-hidden">
          <img
            src={image.download_url}
            alt={image.author}
            className="w-full h-48 object-cover"
          />
          <p className="p-2 text-center text-sm font-medium">
            Photo by: {image.author}
          </p>
        </div>
      ))}
    </div>
  );
};

export default ImageList;

ImageContainer组件

import { Suspense } from "react";
import ImageList from "./ImageList";

const ImageContainer = () => {
  return (
    <div className="p-6 font-sans">
      <h1 className="text-2xl font-bold text-center mb-6">Image Gallery</h1>
      <Suspense
        fallback={
          <div className="text-center text-lg text-black">
            Loading images...
          </div>
        }
      >
      <ImageList />
      </Suspense>
    </div>
  );
};

export default ImageContainer;

环境信息

  • React: 18.3.1
  • @tanstack/react-query: 5.62.7

原因分析

  1. Suspense未参与渲染流程:App组件直接渲染ImageList,而非包裹了Suspense的ImageContainer,导致Suspense组件根本没生效。
  2. React Query默认关闭Suspense支持:v5版本中useQuery默认不启用Suspense模式,必须显式配置suspense: true才能触发React的Suspense机制。
  3. 容错逻辑跳过Suspense触发:未开启Suspense时,data为undefined,代码中用data?.map做了容错,页面仅显示空白而非触发fallback。

修复方案

步骤1:让Suspense生效

修改App组件,将ImageList替换为ImageContainer,确保Suspense包裹数据请求组件:

import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import ImageContainer from "./components/suspense/ImageContainer";

const queryClient = new QueryClient();

const App = () => {
  return (
    <QueryClientProvider client={queryClient}>
      <ImageContainer />
    </QueryClientProvider>
  );
};

export default App;

步骤2:启用React Query的Suspense模式

两种配置方式任选其一:

方式一:全局配置(推荐,所有查询生效)

创建QueryClient时全局开启Suspense:

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      suspense: true,
    },
  },
});

方式二:单个查询配置(仅当前查询生效)

在自定义Hook的useQuery中单独开启:

const usePicsumImages = () => {
  return useQuery<PicsumImage[]>({
    queryKey: ["picsumImages"],
    queryFn: fetchPicsumImages,
    suspense: true,
  });
};

步骤3:优化代码(可选)

启用Suspense后,data不会为undefined,可去掉可选链简化代码:

{data.map((image) => (
  <div key={image.id} className="rounded shadow-md overflow-hidden">
    {/* 原有内容 */}
  </div>
))}

额外优化:处理单张图片加载状态

若需展示单张图片的加载/错误状态,可给img标签添加逻辑:

const ImageItem = ({ image }: { image: PicsumImage }) => {
  const [isLoading, setIsLoading] = useState(true);
  const [hasError, setHasError] = useState(false);

  return (
    <div key={image.id} className="rounded shadow-md overflow-hidden">
      {isLoading && (
        <div className="w-full h-48 bg-gray-200 flex items-center justify-center">
          <span>Loading image...</span>
        </div>
      )}
      {hasError && (
        <div className="w-full h-48 bg-red-100 flex items-center justify-center">
          <span>Failed to load image</span>
        </div>
      )}
      <img
        src={image.download_url}
        alt={image.author}
        className={`w-full h-48 object-cover ${isLoading || hasError ? "hidden" : ""}`}
        onLoad={() => setIsLoading(false)}
        onError={() => {
          setIsLoading(false);
          setHasError(true);
        }}
      />
      <p className="p-2 text-center text-sm font-medium">
        Photo by: {image.author}
      </p>
    </div>
  );
};

内容的提问来源于stack exchange,提问作者Haitam-Elgharras

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 13:43:11