Як працює Context в Server Components?
Як це влаштовано
- Provider можна оголосити на сервері
Ви можете створити контекст і задати
valueв Server Component - всі вкладені Client-компоненти зможуть читати його черезuseContext. - Значення контексту повинне бути серіалізованим
Все, що піде з сервера на клієнт, повинне бути безпечно серіалізоване.
=> Використовуйте примітиви і прості об'єкти/масиви.
Не можна: функції, інстанси класів, проксі, DOM-ноди тощо.
Можна: рядки, числа, булеві,
null, plain-об'єкти, масиви (і те, що ви самі готові перетворити на JSON). - Контекст зверху вниз, але не назад Контекст, заданий на сервері, доступний клієнту. Але стан, змінений у клієнтському Provider'і, не може "підняти" зміни назад на сервер: серверні компоненти вже відрендерені на запиті.
- Client-only Provider не можна імпортувати з сервера
Якщо Provider використовує хуки
useState/useEffectі позначений"use client", імпортувати його напряму з Server Component не можна. Обгортайте дерево через клієнтський "обгортковий" компонент. - Ізоляція по запиту Server Components рендеряться на кожен запит. Контекст, створений на сервері (наприклад, з cookies/headers/БД), автоматично скоупиться на конкретний запит (чудово підходить для locale, auth, feature-flags).
Міні-приклад (Next.js / RSC)
shared/context.ts
javascript
import { createContext } from "react";
export type AppCtx = { locale: string; userName?: string | null };
export const AppContext = createContext<AppCtx>({ locale: "en" });app/layout.tsx (Server Component)
javascript
import { cookies } from "next/headers";
import { AppContext } from "@/shared/context";
export default async function RootLayout({ children }: { children: React.ReactNode }) {
const cookieStore = await cookies();
const locale = cookieStore.get("locale")?.value ?? "en";
// значення повинне бути серіалізованим:
const value = { locale, userName: null };
return (
<html lang={locale}>
<body>
<AppContext.Provider value={value}>
{children}
</AppContext.Provider>
</body>
</html>
);
}components/UserGreeting.tsx ("use client")
javascript
"use client";
import { useContext } from "react";
import { AppContext } from "@/shared/context";
export function UserGreeting() {
const { locale, userName } = useContext(AppContext);
return <p>{userName ? `Hi, ${userName}!` : `Locale: ${locale}`}</p>;
}Тут Provider на сервері віддає value, а клієнтський компонент читає його через useContext.
Практичні поради
- Дробіть контексти за областями (locale/auth/theme), щоб уникнути зайвих перемальовувань клієнтських споживачів.
- Не кладіть у контекст секрети: все, що доступне клієнту, вважається "публічним". Логіку доступу тримайте на сервері, а в контекст передавайте тільки "безпечні" похідні.
- Мемоізуйте великі значення на боці клієнта (якщо Provider клієнтський), щоб не тригерити зайві ререндери (
useMemoдляvalue). - Для динаміки на клієнті (тема, перемикачі) обгортайте потрібну частину дерева клієнтським Provider'ом. Це не зачепить серверні компоненти вище.
Коротка відповідь
Для співбесідиPremium
Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.