Загрузка данных
В Server Components можно делать fetch напрямую и использовать async/await.
Базовый fetch
export default async function Posts() {
const res = await fetch('https://api.example.com/posts');
const posts = await res.json();
return <ul>{posts.map((p) => <li key={p.id}>{p.title}</li>)}</ul>;
}
Компонент — async. Next.js ждёт промис и рендерит результат.
Параллельная загрузка
Не жди по очереди:
// Плохо — последовательно
const user = await getUser();
const posts = await getPosts();
const comments = await getComments();
// Хорошо — параллельно
const [user, posts, comments] = await Promise.all([
getUser(),
getPosts(),
getComments(),
]);
Динамические параметры
export default async function Post({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
const post = await getPost(id);
return <h1>{post.title}</h1>;
}
params — промис, нужно await.
Search params
export default async function Search({
searchParams,
}: {
searchParams: Promise<{ q?: string }>;
}) {
const { q } = await searchParams;
const results = await search(q);
return <ul>{results.map((r) => <li key={r.id}>{r.title}</li>)}</ul>;
}
Доступ к БД
Server Components могут работать с БД напрямую:
import { db } from '@/lib/db';
export default async function Users() {
const users = await db.user.findMany();
return <ul>{users.map((u) => <li key={u.id}>{u.name}</li>)}</ul>;
}
Не нужен API-роут для внутренней загрузки.
Кеширование
По умолчанию fetch кешируется в Next.js:
const res = await fetch('https://api.example.com/data'); // кеш
Отключить:
const res = await fetch('https://api.example.com/data', {
cache: 'no-store',
});
Revalidate через время:
const res = await fetch('https://api.example.com/data', {
next: { revalidate: 3600 }, // 1 час
});
С тегом:
const res = await fetch('https://api.example.com/posts', {
next: { tags: ['posts'] },
});
Перегенерация:
import { revalidateTag } from 'next/cache';
revalidateTag('posts');
Suspense
Оберни медленную часть в <Suspense>:
import { Suspense } from 'react';
export default function Page() {
return (
<>
<h1>Заголовок</h1>
<Suspense fallback={<Skeleton />}>
<SlowComponent />
</Suspense>
</>
);
}
async function SlowComponent() {
const data = await fetchSlowData();
return <div>{data}</div>;
}
Страница показывается сразу, SlowComponent — по мере готовности.
loading.tsx
Автоматический Suspense для страницы:
// app/posts/loading.tsx
export default function Loading() {
return <Skeleton />;
}
Показывается, пока страница грузится.
Обработка ошибок
export default async function Post({ params }) {
const { id } = await params;
const post = await getPost(id);
if (!post) notFound();
return <h1>{post.title}</h1>;
}
Или через error.tsx в папке.
Итоги
- Server Components —
async/awaitнапрямую - Параллельно через
Promise.all paramsиsearchParams— промисы- Кеш
fetchпо умолчанию revalidateи тегиSuspenseдля стриминга