Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Як працює Context в Server Components?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Provider можна оголосити на сервері** - можна створити контекст і задати `value` у Server Component, і всі вкладені **Client**-компоненти зможуть читати його через `useContext`. **Ключове:** значення контексту повинне бути серіалізованим, а стан, змінений у клієнтському Provider, не може "підняти" зміни назад на сервер, бо серверні компоненти вже відрендерені на запиті.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення## Як це влаштовано 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'ом. Це не зачепить серверні компоненти вище.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.