SuperAPI AI Directory

tanstack-query-intent-framework-use-suspense-and-error-0e96f8ec

Frontend & websites

View repository

Updated 10-16 10:59

About this skill

This skill builds on core/fetch-and-observe-queries and lifecycle/prefetch-and-remove-request-waterfalls.

Setup

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}> ) }


## Hooks and Components

### Reset query errors with the boundary

```tsx
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>
  )
}

Use suspense when data must be defined

import { useSuspenseQuery } from '@tanstack/react-query'

export function useTodos() {
  return useSuspenseQuery({
    queryKey: ['todos'],
    queryFn: async () => [{ id: 1 }],
  })
}

Use normal queries for disabled flows

import { useQuery } from '@tanstack/react-query'

export function useMaybeTodo(id: string | undefined) {
  return useQuery({
    queryKey: ['todo', id],
    queryFn: async () => ({ id }),
    enabled: Boolean(id),
  })
}

Common Mistakes

HIGH Disabled suspense query

Wrong:

import { useSuspenseQuery } from '@tanstack/react-query'

export function useTodo(id: string | undefined) {
  return useSuspenseQuery({
    queryKey: ['todo', id],
    queryFn: async () => ({ id }),
    enabled: Boolean(id),
  })
}

Correct:

import { useQuery } from '@tanstack/react-query'

export function useTodo(id: string | undefined) {
  return useQuery({
    queryKey: ['todo', id],
    queryFn: async () => ({ id }),
    enabled: Boolean(id),
  })
}

Suspense hooks guarantee data and do not support conditional disabling like normal queries.

Source: TanStack/query:docs/framework/react/guides/suspense.md

HIGH Missing reset boundary

Wrong:

import { useSuspenseQuery } from '@tanstack/react-query'

export function Todos() {
  const { data } = useSuspenseQuery({
    queryKey: ['todos'],
    queryFn: async () => [{ id: 1 }],
  })
  return <pre>{JSON.stringify(data)}</pre>
}

Correct:

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>
  )
}

Error boundaries need Query reset coordination so failed queries can retry after reset.

Source: TanStack/query:docs/framework/react/reference/QueryErrorResetBoundary.md

MEDIUM query.promise without flag

Wrong:

import { useQuery } from '@tanstack/react-query'

export function useTodoPromise() {
  return useQuery({ queryKey: ['todo', 1], queryFn: async () => ({ id: 1 }) })
    .promise
}

Correct:

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
}

The stable promise property requires the experimental prefetch-in-render flag.

Source: TanStack/query:docs/framework/react/guides/suspense.md

See also: lifecycle/ssr-hydration-and-streaming for streamed hydration constraints.