Skip to main content

Як працює Context в Server Components?

Як це влаштовано

  1. Provider можна оголосити на сервері Ви можете створити контекст і задати value в Server Component - всі вкладені Client-компоненти зможуть читати його через useContext.
  2. Значення контексту повинне бути серіалізованим Все, що піде з сервера на клієнт, повинне бути безпечно серіалізоване. => Використовуйте примітиви і прості об'єкти/масиви. Не можна: функції, інстанси класів, проксі, DOM-ноди тощо. Можна: рядки, числа, булеві, null, plain-об'єкти, масиви (і те, що ви самі готові перетворити на JSON).
  3. Контекст зверху вниз, але не назад Контекст, заданий на сервері, доступний клієнту. Але стан, змінений у клієнтському Provider'і, не може "підняти" зміни назад на сервер: серверні компоненти вже відрендерені на запиті.
  4. Client-only Provider не можна імпортувати з сервера Якщо Provider використовує хуки useState/useEffect і позначений "use client", імпортувати його напряму з Server Component не можна. Обгортайте дерево через клієнтський "обгортковий" компонент.
  5. Ізоляція по запиту 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

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.