Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Context і SSR». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**React Context** дозволяє зберігати "глобальні" дані (`user`, `theme`, `locale`, `auth`) і передавати їх глибоко в дерево компонентів без пропів. **Ключове:** так, контекст чудово працює з SSR, якщо `Provider` створюється всередині кожного SSR-запиту (унікальний екземпляр), інакше дані можуть "протекти" між користувачами.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення## Що робить React Context React Context дозволяє: - зберігати "глобальні" дані (наприклад, `user`, `theme`, `locale`, `auth`), - передавати їх глибоко в дерево компонентів **без пропів**. Приклад (клієнтський): ```javascript const ThemeContext = createContext('light'); function App() { return ( <ThemeContext.Provider value="dark"> <Page /> </ThemeContext.Provider> ); } ``` --- ## Що відбувається при SSR При **Server-Side Rendering** React: 1. створює React-дерево на сервері, 2. **рендерить його з поточними контекстними значеннями** в HTML, 3. відправляє HTML клієнту, 4. клієнт виконує **hydration** - "оживляє" той самий контекст на стороні браузера. Тобто **Context бере участь у рендерингу HTML**, і саме його значення визначають, **що сервер покладе в HTML** (наприклад, темну тему або ім'я користувача). --- ## Так, контекст працює при SSR Приклад: ```javascript // theme-context.ts import { createContext, useContext } from "react"; export const ThemeContext = createContext("light"); export const useTheme = () => useContext(ThemeContext); ``` ```javascript // App.tsx import { ThemeContext } from "./theme-context"; export function App({ theme }: { theme: string }) { return ( <ThemeContext.Provider value={theme}> <Page /> </ThemeContext.Provider> ); } ``` ```javascript // server.tsx import { renderToString } from "react-dom/server"; import { App } from "./App"; app.get("/", (req, res) => { const html = renderToString(<App theme="dark" />); res.send(html); }); ``` Сервер рендерить сторінку одразу в темній темі. При гідратації клієнт отримує той самий контекст (`value="dark"`), і React не робить повторний рендер. --- ## Але! Контекст має бути **унікальним для кожного запиту** Контекст на сервері - **глобальний об'єкт React**, тому якщо ти використовуєш **один і той самий екземпляр** для всіх користувачів, він може "протекти" між запитами. ### Приклад небезпечного коду: ```javascript // спільний контекст для всіх користувачів - небезпечно const UserContext = createContext(null); app.get("/", (req, res) => { UserContext._currentValue = { id: 1, name: "Alice" }; const html = renderToString(<App />); res.send(html); }); ``` → Якщо наступний користувач - "Bob", він може побачити дані "Alice". Це **race condition між запитами**. --- ## Як правильно: "ізолювати контекст на запит" Створюй `Provider` **всередині рендеру кожного SSR-запиту**: ```javascript const UserContext = createContext(null); app.get("/", async (req, res) => { const user = await getUserFromSession(req.cookies.token); const html = renderToString( <UserContext.Provider value={user}> <App /> </UserContext.Provider> ); res.send(html); }); ``` Так у кожного запиту буде **свій екземпляр контексту** і свої дані. --- ## Як це працює в Next.js У Next.js це все автоматизовано: ```javascript // layout.tsx export default function RootLayout({ children }) { const theme = cookies().get('theme')?.value || 'light'; return ( <ThemeContext.Provider value={theme}> {children} </ThemeContext.Provider> ); } ``` - На сервері `cookies()` читаються при кожному запиті. - Контекст створюється **унікально для рендеру сторінки**. - На клієнті він гідратується без розсинхронізації. --- ## Що важливо пам'ятати | Особливість | Пояснення | |---|---| | Працює при SSR | Context бере участь у серверному рендерингу | | Передається в HTML | Контекстне значення впливає на HTML, який бачить користувач | | Не можна зберігати глобально | Інакше дані "протечуть" між користувачами | | Гідратація має збігатися | Контекст на клієнті повинен мати те саме значення, що й на сервері | | Можна використовувати кілька контекстів | Наприклад, `UserContext`, `ThemeContext`, `LocaleContext` одночасно | --- ## Висновок **React Context чудово працює з SSR**, якщо: 1. Ти створюєш `Provider` всередині кожного SSR-запиту (унікальний екземпляр). 2. Значення контексту збігаються на сервері і клієнті (інакше гідратація "зламається"). 3. Не виконуєш DOM-операції всередині контекстних ефектів на сервері. --- ### Короткий рецепт безпечного SSR + Context ```javascript // server.tsx renderToString( <AuthProvider user={user}> <ThemeProvider theme="dark"> <App /> </ThemeProvider> </AuthProvider> ); ``` - Кожному запиту - свій `Provider` - Контексти передаються через props - На клієнті і сервері - однакове дерево провайдерівДля рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.