Context передає значення від провайдера до будь-якого нащадка без props на кожному рівні: тема, мова, поточний користувач, налаштування.
const ThemeContext = createContext('light');
<ThemeContext value={theme}> {/* React 19; раніше ThemeContext.Provider */}
<App />
</ThemeContext>
const theme = useContext(ThemeContext);
Чому всі споживачі перерендерюються: коли значення провайдера змінилося (порівняння через Object.is), React перерендерює кожен компонент, що читає цей контекст, навіть якщо використовує лише частину значення. Вибіркової підписки на поле в контексту немає.
Типова пастка - новий об'єкт на кожен рендер:
<AuthContext value={{ user, login, logout }}>
Кожен рендер провайдера створює новий об'єкт - і всі споживачі перерендерюються, навіть якщо user не змінився.
Як з цим жити:
- Мемоізувати значення:
const value = useMemo(() => ({ user, login, logout }), [user]), функції - черезuseCallback. - Розділяти контексти: дані, що часто змінюються, окремо від рідкісних; стан окремо від функцій-диспетчерів (
StateContextіDispatchContext). - Не тримати в контексті часто змінюваний стан великої частини застосунку (значення полів форми, позиція курсору). Для такого краще стор з селекторами (Zustand, Redux, Jotai), де компонент підписується лише на потрібну частину.
Context - не менеджер стану, а механізм передачі. Він чудово працює для рідко змінюваних значень і погано масштабується для глобального стану, що часто оновлюється.
Докладніше в документації: Передача даних глибоко через Context