Перейти к содержимому

19. defer и Await

defer позволяет возвращать из loader незавершённые Promise, которые будут стримиться клиенту. Компонент <Await> с <Suspense> рендерит данные по мере их поступления.

Без 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>
);
}
<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 — стримим
});
}
СитуацияРешение
Все данные быстрыеОбычный json()
Одна секция медленнаяdefer() для неё
Данные можно показать частичноdefer() для медленных
Данные критически важны для SEOawait + json()