19. defer и Await
defer позволяет возвращать из loader незавершённые Promise, которые будут стримиться клиенту. Компонент <Await> с <Suspense> рендерит данные по мере их поступления.
Проблема, которую решает defer
Заголовок раздела «Проблема, которую решает defer»Без defer: loader ждёт ВСЕ данные → 3000ms задержка → страница появляется целиком
С defer: loader возвращает быстрые данные + Promise → 500ms → базовая страница медленные данные стримятся → 3000ms → секция обновляетсяБазовое использование
Заголовок раздела «Базовое использование»import { defer, json } from "@remix-run/node";import { Await, useLoaderData } from "@remix-run/react";import { Suspense } from "react";
export async function loader() { // Быстрые данные — ждём const criticalData = await getUserData(); // ~100ms
// Медленные — не ждём, передаём Promise const slowData = getRecommendations(); // ~3000ms, без await!
return defer({ criticalData, // сразу доступно slowData, // Promise, стримится });}
export default function Page() { const { criticalData, slowData } = useLoaderData<typeof loader>();
return ( <div> {/* Рендерится сразу */} <h1>{criticalData.user.name}</h1>
{/* Рендерится когда Promise разрешится */} <Suspense fallback={<p>⏳ Загрузка рекомендаций...</p>}> <Await resolve={slowData}> {(data) => ( <div> {data.recommendations.map(r => ( <RecommendationCard key={r.id} item={r} /> ))} </div> )} </Await> </Suspense> </div> );}Обработка ошибок в defer
Заголовок раздела «Обработка ошибок в defer»<Suspense fallback={<Skeleton />}> <Await resolve={slowData} errorElement={<p>Не удалось загрузить рекомендации</p>} > {(data) => <RecommendationList data={data} />} </Await></Suspense>Несколько независимых секций
Заголовок раздела «Несколько независимых секций»export async function loader() { const [user, stats, feed, ads] = await Promise.all([ getUser(), // 50ms — ждём getStats(), // 100ms — ждём ]);
return defer({ user, stats, feed: getFeed(), // 1500ms — стримим ads: getAds(), // 2000ms — стримим recommendations: getRecommendations(), // 3000ms — стримим });}Когда использовать defer
Заголовок раздела «Когда использовать defer»| Ситуация | Решение |
|---|---|
| Все данные быстрые | Обычный json() |
| Одна секция медленная | defer() для неё |
| Данные можно показать частично | defer() для медленных |
| Данные критически важны для SEO | await + json() |