Skip to main content

Context і SSR

Що робить 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() читаються при кожному запиті.
  • Контекст створюється унікально для рендеру сторінки.
  • На клієнті він гідратується без розсинхронізації.

Що важливо пам'ятати

ОсобливістьПояснення
Працює при SSRContext бере участь у серверному рендерингу
Передається в 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
  • На клієнті і сервері - однакове дерево провайдерів

Коротка відповідь

Для співбесіди
Premium

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