本技能是 TanStack Query 在 React 中处理 Suspense 与错误边界的使用指南,建立在 core/fetch-and-observe-queries 与 lifecycle/prefetch-and-remove-request-waterfalls 之上。
用途
用于在使用以下 API 时获得正确写法:useSuspenseQuery、useSuspenseQueries、useSuspenseInfiniteQuery、QueryErrorResetBoundary、throwOnError、useQueryErrorResetBoundary 等。目标是让采用 Suspense 模式的数据获取既能保证数据已定义,又能与错误边界协同完成失败重试。
主要能力与使用方式
基础设置
使用 React 的 Suspense 组件包裹使用 useSuspenseQuery 的组件,并用 fallback 指定加载态:
import { Suspense } from 'react'
import { useSuspenseQuery } from '@tanstack/react-query'
function Todos() {
const { data } = useSuspenseQuery({
queryKey: ['todos'],
queryFn: async () => [{ id: 1 }],
})
return <pre>{JSON.stringify(data)}</pre>
}
export function App() {
return (
<Suspense fallback={Loading}>
<Todos />
</Suspense>
)
}
用边界重置查询错误
借助 QueryErrorResetBoundary 与 react-error-boundary 的 ErrorBoundary,在出错时提供重试按钮,重置时调用 reset 以让失败的查询可以重新尝试:
import { QueryErrorResetBoundary } from '@tanstack/react-query'
import { ErrorBoundary } from 'react-error-boundary'
export function QueryErrorBoundary(props: { children: React.ReactNode }) {
return (
<QueryErrorResetBoundary>
{({ reset }) => (
<ErrorBoundary
onReset={reset}
fallbackRender={({ resetErrorBoundary }) => (
<button onClick={resetErrorBoundary}>Retry</button>
)}
>
{props.children}
</ErrorBoundary>
)}
</QueryErrorResetBoundary>
)
}
数据必须存在时使用 suspense
import { useSuspenseQuery } from '@tanstack/react-query'
export function useTodos() {
return useSuspenseQuery({
queryKey: ['todos'],
queryFn: async () => [{ id: 1 }],
})
}
可禁用流程使用普通查询
当查询需要按条件启用或禁用时,应改用普通的 useQuery:
import { useQuery } from '@tanstack/react-query'
export function useMaybeTodo(id: string | undefined) {
return useQuery({
queryKey: ['todo', id],
queryFn: async () => ({ id }),
enabled: Boolean(id),
})
}
常见错误
HIGH:对 suspense 查询使用 disabled
错误写法是对 useSuspenseQuery 传入 enabled。Suspense 钩子保证有数据,不支持像普通查询那样按条件禁用。正确做法是改用 useQuery 并设置 enabled。
// 错误
import { useSuspenseQuery } from '@tanstack/react-query'
export function useTodo(id: string | undefined) {
return useSuspenseQuery({
queryKey: ['todo', id],
queryFn: async () => ({ id }),
enabled: Boolean(id),
})
}
// 正确
import { useQuery } from '@tanstack/react-query'
export function useTodo(id: string | undefined) {
return useQuery({
queryKey: ['todo', id],
queryFn: async () => ({ id }),
enabled: Boolean(id),
})
}
来源:TanStack/query:docs/framework/react/guides/suspense.md
HIGH:缺少重置边界
错误做法是直接使用 useSuspenseQuery 而不提供 QueryErrorResetBoundary。正确做法是用 QueryErrorResetBoundary 包裹 ErrorBoundary,使失败的查询在重置后可以重试:
import { QueryErrorResetBoundary } from '@tanstack/react-query'
import { ErrorBoundary } from 'react-error-boundary'
export function WrappedTodos(props: { children: React.ReactNode }) {
return (
<QueryErrorResetBoundary>
{({ reset }) => (
<ErrorBoundary onReset={reset} fallback={<p>Error</p>}>
{props.children}
</ErrorBoundary>
)}
</QueryErrorResetBoundary>
)
}
来源:TanStack/query:docs/framework/react/reference/QueryErrorResetBoundary.md
MEDIUM:未开启标志就使用 query.promise
稳定的 promise 属性需要实验性的 prefetch-in-render 标志。必须在 QueryClient 的 defaultOptions 中设置 experimental_prefetchInRender: true,否则不应依赖 query.promise:
import { QueryClient, useQuery } from '@tanstack/react-query'
export const queryClient = new QueryClient({
defaultOptions: { queries: { experimental_prefetchInRender: true } },
})
export function useTodoPromise() {
return useQuery({ queryKey: ['todo', 1], queryFn: async () => ({ id: 1 }) })
.promise
}
来源:TanStack/query:docs/framework/react/guides/suspense.md
适用场景
适用于使用 React Suspense 模式与 TanStack Query 进行数据获取、并需要统一错误处理与重试的前端项目。
限制
Suspense 钩子不支持 enabled 这类条件禁用;需要禁用时改用普通查询。稳定 promise 需实验性标志支持。另请参考 lifecycle/ssr-hydration-and-streaming 了解流式注水的约束。其余扩展细节文档暂未提供。