Что такое React Server Components?
React Server Components (RSC) — это новая архитектура React, позволяющая рендерить компоненты на сервере без отправки их JavaScript-кода клиенту. Это революционный подход, который меняет способ построения React-приложений.
Ключевые преимущества
- Меньший размер бандла — серверный код не попадает к клиенту
- Прямой доступ к данным — можно обращаться к БД напрямую
- Улучшенный SEO — контент рендерится на сервере
- Быстрая начальная загрузка — меньше JavaScript для парсинга
Server vs Client Components
// ✅ Server Component (по умолчанию в Next.js App Router)
import { db } from '@/lib/database';
async function ProductList() {
// Прямой доступ к базе данных!
const products = await db.product.findMany({
where: { isActive: true },
include: { category: true }
});
return (
<ul>
{products.map(product => (
<li key={product.id}>
{product.name} - {product.price}₽
</li>
))}
</ul>
);
}
// ❌ Client Component — нужна директива 'use client'
'use client';
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(c => c + 1)}>
Clicked {count} times
</button>
);
}
Когда использовать Client Components
// Нужен 'use client' для:
// 1. Интерактивность
'use client';
function LikeButton() {
const [liked, setLiked] = useState(false);
return <button onClick={() => setLiked(!liked)}>❤️</button>;
}
// 2. Браузерные API
'use client';
function WindowSize() {
const [size, setSize] = useState({ width: 0, height: 0 });
useEffect(() => {
const handleResize = () => {
setSize({ width: window.innerWidth, height: window.innerHeight });
};
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return <div>{size.width} x {size.height}</div>;
}
// 3. Хуки React (useState, useEffect, useContext)
'use client';
function ThemeToggle() {
const { theme, setTheme } = useContext(ThemeContext);
return <button onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}>Toggle</button>;
}
Паттерн композиции
// app/products/[id]/page.tsx (Server Component)
import { db } from '@/lib/database';
import { AddToCartButton } from '@/components/AddToCartButton';
export default async function ProductPage({ params }: { params: { id: string } }) {
const product = await db.product.findUnique({
where: { id: params.id },
include: { images: true }
});
if (!product) return notFound();
return (
<div className="grid grid-cols-2 gap-8">
<ProductGallery images={product.images} />
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
<p className="text-2xl font-bold">{product.price}₽</p>
<AddToCartButton productId={product.id} />
</div>
</div>
);
}
// components/AddToCartButton.tsx
'use client';
import { useState } from 'react';
export function AddToCartButton({ productId }: { productId: string }) {
const [pending, setPending] = useState(false);
const handleClick = async () => {
setPending(true);
await addToCart(productId);
setPending(false);
};
return (
<button onClick={handleClick} disabled={pending}>
{pending ? 'Добавление...' : 'В корзину'}
</button>
);
}
Server Actions
// app/actions.ts
'use server';
import { db } from '@/lib/database';
import { revalidatePath } from 'next/cache';
export async function createPost(formData: FormData) {
const title = formData.get('title') as string;
const content = formData.get('content') as string;
await db.post.create({
data: { title, content }
});
revalidatePath('/posts');
}
// Использование в форме
export default function NewPostPage() {
return (
<form action={createPost}>
<input name="title" placeholder="Заголовок" required />
<textarea name="content" placeholder="Содержание" required />
<button type="submit">Создать</button>
</form>
);
}
Streaming и Suspense
import { Suspense } from 'react';
export default function DashboardPage() {
return (
<div>
<h1>Dashboard</h1>
<Suspense fallback={<StatsSkeleton />}>
<Stats />
</Suspense>
<Suspense fallback={<ChartSkeleton />}>
<RevenueChart />
</Suspense>
<Suspense fallback={<TableSkeleton />}>
<RecentOrders />
</Suspense>
</div>
);
}
async function Stats() {
const stats = await getStats();
return <StatsGrid data={stats} />;
}
async function RevenueChart() {
const data = await getRevenueData();
return <Chart data={data} />;
}
Серверный рендеринг vs Клиентский компонент
Понимание разницы между серверным рендерингом и клиентским компонентом — ключ к эффективной архитектуре. Серверный рендеринг происходит на сервере, результат отправляется как HTML. Клиентский компонент требует JavaScript для работы.
Гидратация и производительность
Гидратация — процесс "оживления" серверного HTML на клиенте. Чем меньше клиентских компонентов, тем быстрее гидратация и лучше производительность.
Работа с базой данных
Server Components позволяют напрямую обращаться к базе данных без создания API endpoints. Используйте fetch для внешних API или ORM (Prisma, Drizzle) для базы данных. Кэширование запросов происходит автоматически.
Композиция компонентов
Правильная композиция — Server Components содержат Client Components, но не наоборот. Это позволяет минимизировать клиентский JavaScript и улучшить SEO.
Заключение
React Server Components — это не просто оптимизация, это новый способ думать о React-приложениях. Начните с простого правила: всё по умолчанию — Server Component, и добавляйте 'use client' только когда это действительно необходимо.
Server Components + Server Actions = полноценный fullstack на React без API routes.