8. Query: Suspense режим
Традиционный подход vs Suspense
Заголовок раздела «Традиционный подход vs Suspense»Традиционный подход с useQuery требует явной обработки каждого состояния:
function UserList() { const { data, isLoading, isError, error } = useQuery(...) if (isLoading) return <Spinner /> if (isError) return <Error message={error.message} /> return <List data={data} />}Suspense подход разделяет ответственность: компонент всегда получает данные, а загрузка и ошибки обрабатываются выше в дереве компонентов:
// Компонент получает данные напрямую — нет isLoading, нет isErrorfunction UserList() { const { data } = useSuspenseQuery(...) return <List data={data} /> // data всегда определена}
// Родитель обрабатывает состоянияfunction App() { return ( <ErrorBoundary fallback={<Error />}> <Suspense fallback={<Spinner />}> <UserList /> </Suspense> </ErrorBoundary> )}useSuspenseQuery
Заголовок раздела «useSuspenseQuery»Идентичен useQuery, но с ключевым отличием: гарантирует, что data всегда определена.
Если данных нет — компонент “suspends” (приостанавливается), и React показывает fallback из <Suspense>.
import { useSuspenseQuery } from '@tanstack/react-query'
function UserProfile({ userId }: { userId: number }) { // data гарантированно User, не User | undefined const { data } = useSuspenseQuery({ queryKey: ['user', userId], queryFn: () => fetchUser(userId), })
// Никакого isLoading/isError — React Suspense обрабатывает это return <div>{data.name}</div>}ErrorBoundary
Заголовок раздела «ErrorBoundary»Для перехвата ошибок в Suspense-режиме используется ErrorBoundary:
import { ErrorBoundary } from 'react-error-boundary'
function App() { return ( <ErrorBoundary fallbackRender={({ error, resetErrorBoundary }) => ( <div> <p>Ошибка: {error.message}</p> <button onClick={resetErrorBoundary}>Повторить</button> </div> )} > <Suspense fallback={<Spinner />}> <UserList /> </Suspense> </ErrorBoundary> )}useSuspenseQueries и useSuspenseInfiniteQuery
Заголовок раздела «useSuspenseQueries и useSuspenseInfiniteQuery»// Параллельные запросы с Suspenseconst [{ data: users }, { data: posts }] = useSuspenseQueries({ queries: [ { queryKey: ['users'], queryFn: getUsers }, { queryKey: ['posts'], queryFn: getPosts }, ],})
// Бесконечный скролл с Suspenseconst { data } = useSuspenseInfiniteQuery({ queryKey: ['posts'], queryFn: fetchPosts, initialPageParam: 0, getNextPageParam: (last) => last.nextPage,})Когда использовать Suspense?
Заголовок раздела «Когда использовать Suspense?»Используйте Suspense, когда:
- Хотите чистые компоненты без if/else для загрузки
- Используете параллельные запросы
- Работаете с React Server Components
- Хотите “waterfall-free” загрузку
Оставайтесь с useQuery, когда:
- Нужен тонкий контроль над состояниями загрузки
- Partial loading (показываем данные по частям)
- Компонент должен показывать что-то даже во время фоновой загрузки