译零 TransZero · TanStack Query 中文文档
主站

Suspense

更新于 2026-11-02 React 查看英文原文 ↗

React Query 也可与 React 的 Suspense 数据拉取(Data Fetching)API 配合使用。为此,我们提供了专用的 Hook:

启用 suspense 模式后,status 状态和 error 对象将不再需要,取而代之的是使用 React.Suspense 组件(包括使用 fallback 属性,以及借助 React 错误边界捕获错误)。请阅读 重置错误边界,并查看 Suspense 示例,了解如何配置 suspense 模式。 //: # 'Info'

若希望修改(mutation)也像查询(query)一样将错误传播至最近的错误边界,你也可以将 throwOnError 选项设为 true。

为某个查询启用 suspense 模式:

TypeScript
1import { useSuspenseQuery } from '@tanstack/react-query'2 3const { data } = useSuspenseQuery({ queryKey, queryFn })

该方式在 TypeScript 中表现良好,因为 data 被保证已定义(错误与加载状态均由 Suspense 和错误边界处理)。

但反过来说,你因此无法条件性地启用或禁用该查询。对于依赖型查询(dependent Queries),这通常并非必需——因为在 suspense 模式下,组件内所有查询会以串行方式拉取。

此查询也不存在 placeholderData。为防止更新期间 UI 被 fallback 替换,请将更改 QueryKey 的更新操作包裹在 startTransition 中。 //: # 'PlaceholderData'

throwOnError 默认行为

并非所有错误都会默认抛出至最近的错误边界——我们仅在无其他可展示数据时才抛出错误。这意味着:只要查询曾在缓存中成功获取过数据,组件就会正常渲染,即使当前数据已 stale。因此 throwOnError 的默认值为:

Code
1throwOnError: (error, query) => typeof query.state.data === 'undefined'

由于你无法更改 throwOnError(否则可能导致 data 变为潜在的 undefined),若希望所有错误均由错误边界统一处理,则需手动抛出错误:

TypeScript
1import { useSuspenseQuery } from '@tanstack/react-query'2 3const { data, error, isFetching } = useSuspenseQuery({ queryKey, queryFn })4 5if (error && !isFetching) {6  throw error7}8 9// continue rendering data

重置错误边界

无论你在查询中使用 suspense 还是 throwOnError,当发生错误后重新渲染时,都需要一种机制告知查询:你希望重试。

查询错误可通过 QueryErrorResetBoundary 组件或 useQueryErrorResetBoundary Hook 重置。

使用组件时,它将在该组件作用域内重置所有查询错误:

TypeScript
1import { QueryErrorResetBoundary } from '@tanstack/react-query'2import { ErrorBoundary } from 'react-error-boundary'3 4const App = () => (5  <QueryErrorResetBoundary>6    {({ reset }) => (7      <ErrorBoundary8        onReset={reset}9        fallbackRender={({ resetErrorBoundary }) => (10          <div>11            There was an error!12            <button onClick={() => resetErrorBoundary()}>Try again</button>13          </div>14        )}15      >16        <Page />17      </ErrorBoundary>18    )}19  </QueryErrorResetBoundary>20)

使用 Hook 时,它将在最近的 QueryErrorResetBoundary 作用域内重置所有查询错误;若未定义任何边界,则全局重置:

TypeScript
1import { useQueryErrorResetBoundary } from '@tanstack/react-query'2import { ErrorBoundary } from 'react-error-boundary'3 4const App = () => {5  const { reset } = useQueryErrorResetBoundary()6  return (7    <ErrorBoundary8      onReset={reset}9      fallbackRender={({ resetErrorBoundary }) => (10        <div>11          There was an error!12          <button onClick={() => resetErrorBoundary()}>Try again</button>13        </div>14      )}15    >16      <Page />17    </ErrorBoundary>18  )19}

渲染时拉取 vs 边渲染边拉取

开箱即用时,React Query 在 suspense 模式下天然适配 渲染时拉取(Fetch-on-render) 场景,无需额外配置。这意味着:当组件尝试挂载时,它们会触发查询拉取并进入 suspend 状态,但仅发生在你导入并挂载这些组件之后。若想进阶实现 边渲染边拉取(Render-as-you-fetch) 模型,我们建议在路由回调和/或用户交互事件中实现 预取(Prefetching),以便在组件挂载前、甚至在其父组件导入或挂载前就启动查询加载。

服务端 Suspense 与流式传输(streaming)

若你正在使用 NextJs,可尝试我们的 实验性 服务端 Suspense 集成:@tanstack/react-query-next-experimental。该包允许你在客户端组件中直接调用 useSuspenseQuery 实现服务端数据拉取。随后,结果将随 SuspenseBoundary 的解析过程从服务端流式传输至客户端。

要实现此功能,请将你的应用包裹在 ReactQueryStreamedHydration 组件中:

TypeScript
1// app/providers.tsx2'use client'3 4import {5  environmentManager,6  QueryClient,7  QueryClientProvider,8} from '@tanstack/react-query'9import * as React from 'react'10import { ReactQueryStreamedHydration } from '@tanstack/react-query-next-experimental'11 12function makeQueryClient() {13  return new QueryClient({14    defaultOptions: {15      queries: {16        // With SSR, we usually want to set some default staleTime17        // above 0 to avoid refetching immediately on the client18        staleTime: 60 * 1000,19      },20    },21  })22}23 24let browserQueryClient: QueryClient | undefined = undefined25 26function getQueryClient() {27  if (environmentManager.isServer()) {28    // Server: always make a new query client29    return makeQueryClient()30  } else {31    // Browser: make a new query client if we don't already have one32    // This is very important, so we don't re-make a new client if React33    // suspends during the initial render. This may not be needed if we34    // have a suspense boundary BELOW the creation of the query client35    if (!browserQueryClient) browserQueryClient = makeQueryClient()36    return browserQueryClient37  }38}39 40export function Providers(props: { children: React.ReactNode }) {41  // NOTE: Avoid useState when initializing the query client if you don't42  //       have a suspense boundary between this and the code that may43  //       suspend because React will throw away the client on the initial44  //       render if it suspends and there is no boundary45  const queryClient = getQueryClient()46 47  return (48    <QueryClientProvider client={queryClient}>49      <ReactQueryStreamedHydration>50        {props.children}51      </ReactQueryStreamedHydration>52    </QueryClientProvider>53  )54}

欲了解更多信息,请参阅 Next.js Suspense 流式传输示例 和 高级渲染与注水(Hydration)指南。 //: # 'Streaming'

本页为 TanStack Query 文档(React 版)的非官方中文翻译,原文由上游团队维护,遵循 MIT 许可。 译文如有偏差,以英文原文为准。

译自上游 59d11de