10. Query: prefetching и SSR
Зачем нужен prefetching?
Заголовок раздела «Зачем нужен prefetching?»Prefetching — загрузка данных заранее, до того как они понадобятся. Это устраняет задержку загрузки при навигации: данные уже в кэше, компонент рендерится мгновенно.
queryClient.prefetchQuery
Заголовок раздела «queryClient.prefetchQuery»const queryClient = useQueryClient()
// Предзагрузка данных при hover на ссылкуconst handleMouseEnter = () => { queryClient.prefetchQuery({ queryKey: ['user', userId], queryFn: () => fetchUser(userId), staleTime: 1000 * 60, // Не предзагружать если данные свежие })}При hover пользователя на ссылку — данные уже загружаются. При клике — мгновенный переход.
Prefetch в роутерах
Заголовок раздела «Prefetch в роутерах»Многие роутеры поддерживают prefetch на уровне маршрутизации:
// TanStack Router loaderexport const Route = createFileRoute('/users/$userId')({ loader: ({ context: { queryClient }, params }) => queryClient.ensureQueryData({ queryKey: ['user', params.userId], queryFn: () => fetchUser(params.userId), }),})ensureQueryData — как prefetchQuery, но возвращает данные из кэша если они свежие.
SSR: dehydrate и hydrate
Заголовок раздела «SSR: dehydrate и hydrate»Для серверного рендеринга нужно передать данные из сервера в клиент:
// Сервер (Next.js, Remix, TanStack Start)export async function getServerSideProps() { const queryClient = new QueryClient()
await queryClient.prefetchQuery({ queryKey: ['users'], queryFn: getUsers, })
return { props: { dehydratedState: dehydrate(queryClient), }, }}
// Клиентfunction App({ dehydratedState }) { return ( <HydrationBoundary state={dehydratedState}> <Users /> </HydrationBoundary> )}initialData
Заголовок раздела «initialData»Позволяет передать начальные данные напрямую в запрос:
const { data } = useQuery({ queryKey: ['user', userId], queryFn: fetchUser, initialData: userFromSSR, // Данные из SSR initialDataUpdatedAt: Date.now(), // Когда данные были получены})initialData vs placeholderData:
initialData— реальные данные, кэшируются, влияют на staleTimeplaceholderData— временные данные, не кэшируются, заменяются при загрузке
placeholderData
Заголовок раздела «placeholderData»useQuery({ queryKey: ['user', userId], queryFn: fetchUser, placeholderData: (previousData) => previousData, // Показываем старые данные при смене userId})Особенно полезно при пагинации — показываем данные текущей страницы пока грузится следующая.