Доки по разработке
This project is maintained by teniryte
Реальные экраны редко состоят из одного запроса. Обычно нужно:
useQueryЕсли запросов мало и они независимы, можно просто написать их рядом:
const userQuery = useQuery(userOptions(userId))
const statsQuery = useQuery(statsOptions(userId))
const notificationsQuery = useQuery(notificationsOptions(userId))
Такой код уже будет работать параллельно.
useQueriesКогда запросов много или список динамический, используйте useQueries.
import { useQueries } from '@tanstack/react-query'
function Dashboard({ userId }: { userId: string }) {
const results = useQueries({
queries: [
userOptions(userId),
statsOptions(userId),
notificationsOptions(userId),
],
})
const [user, stats, notifications] = results
if (results.some((query) => query.isPending)) return <DashboardSkeleton />
if (results.some((query) => query.isError)) return <ErrorState />
return (
<DashboardView
user={user.data}
stats={stats.data}
notifications={notifications.data}
/>
)
}
combinecombine позволяет агрегировать результат в одну структуру:
const dashboard = useQueries({
queries: [userOptions(userId), statsOptions(userId)],
combine: (results) => ({
isPending: results.some((query) => query.isPending),
isError: results.some((query) => query.isError),
data: {
user: results[0].data,
stats: results[1].data,
},
}),
})
Это уменьшает шум в компоненте и особенно полезно для container-компонентов.
enabledКлассический случай:
const userQuery = useQuery(userOptions(userId))
const projectsQuery = useQuery({
...projectsOptions(userQuery.data?.teamId ?? ''),
enabled: !!userQuery.data?.teamId,
})
Так второй запрос:
useEffectskipToken для зависимых запросовconst projectQuery = useQuery({
queryKey: ['projects', teamId],
queryFn: teamId ? () => fetchProjects(teamId) : skipToken,
})
Подходит, если нужен более строгий TypeScript-контроль.
Плохой сценарий:
Так экран становится заметно медленнее, чем мог бы быть.
Что делать:
Promise.all из ensureQueryDataawait Promise.all([
queryClient.ensureQueryData(userOptions(userId)),
queryClient.ensureQueryData(statsOptions(userId)),
queryClient.ensureQueryData(notificationsOptions(userId)),
])
Так компонент уже подписывается на готовый кеш, а не ждёт цепочку запросов на клиенте.
useSuspenseQueriesЕсли экран работает через Suspense:
import { useSuspenseQueries } from '@tanstack/react-query'
const [user, stats] = useSuspenseQueries({
queries: [userOptions(userId), statsOptions(userId)],
})
Ограничения актуального API:
enabledplaceholderDatathrowOnErrorПрактический вывод: useSuspenseQueries хорош для набора запросов, которые точно должны стартовать сразу.
const orders = useQueries({
queries: orderIds.map((id) => orderOptions(id)),
})
Подводные камни:
queries должен быть детерминированнымЕсли на экране 10-20 мелких запросов, это не всегда повод писать 20 useQuery. Иногда лучше сделать один агрегированный endpoint.
Признаки, что стоит объединить:
useEffect(() => refetch(), [deps]) для зависимостей. Почти всегда это должен быть queryKey или enabled.combine.