Коротка відповідь
juniorReact Context — це механізм, що дозволяє передавати дані через дерево компонентів без явного пропс‑передавання. Я використовую Context для глобальних налаштувань, теми та аутентифікації, коли дані потрібні багатьом глибоко вкладеним компонентам. Context підходить, коли дані змінюються рідко і не потребують частих оновлень, щоб уникнути непотрібних перерендерів.
Повне пояснення
Що це і навіщо
React Context – API, що забезпечує доступ до значень (state, функцій) з будь‑якого рівня компонентної ієрархії без пропс‑передавання. Використовується, коли:
- Дані потрібні багатьом компонентам (наприклад, користувач, тема, локалізація).
- Змінюються рідко або мають глобальний характер.
Ключові принципи
- Provider огортає дерево і передає значення.
- Consumer або
useContextчитають це значення. - Context не замінює Redux чи Zustand, а доповнює їх для простих випадків.
Як це працює
- Створюється контекст:
const ThemeContext = React.createContext(defaultValue);. - У верхньому компоненті обгортаємо дерево:
<ThemeContext.Provider value={theme}>. - У дочірніх компонентах читаємо:
const theme = useContext(ThemeContext);.
Практика і реалізація
// theme-context.tsx
import { createContext, useState } from 'react';
export const ThemeContext = createContext({ theme: 'light', toggle: () => {} });
export const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useState('light');
const toggle = () => setTheme(t => (t === 'light' ? 'dark' : 'light'));
return <ThemeContext.Provider value={{ theme, toggle }}>{children}</ThemeContext.Provider>;
};
// component.tsx
import { useContext } from 'react';
import { ThemeContext } from './theme-context';
const Button = () => {
const { theme, toggle } = useContext(ThemeContext);
return <button onClick={toggle}>Тема: {theme}</button>;
};
Тестування
Для Context‑тестів використовуйте render з @testing-library/react і обгорніть компонент у Provider.
import { render, screen } from '@testing-library/react';
import { ThemeProvider } from './theme-context';
import Button from './component';
test('toggle theme', () => {
render(<ThemeProvider><Button /></ThemeProvider>);
const btn = screen.getByRole('button');
expect(btn).toHaveTextContent(/light/);
fireEvent.click(btn);
expect(btn).toHaveTextContent(/dark/);
});
Особливості в контексті React
- Context не виконує оптимізацію перерендерів; кожен оновлений контекст тригерить рендер усіх Consumer.
- Для частих змін використовуйте
useReducerабо зовнішні state‑менеджери. - Можна комбінувати Context з
React.memoабоuseMemoдля мінімізації перерендерів.
Оптимізація, типові помилки та edge cases
- Надмірне використання: Context для кожного маленького state → збільшує рендери.
- Відсутність типізації: у TS варто визначити інтерфейс значення.
- Проблеми з SSR: Context‑значення можуть бути різними на сервері і клієнті.
- Проблеми з кешуванням: Якщо value‑об’єкт створюється в компоненті, він змінюється при кожному рендері.
- Відсутність fallback: Якщо Consumer використовується поза Provider, отримаєте defaultValue.
- Проблеми з оновленням: Якщо функція
toggleстворюється в компоненті, її референс зміниться. - Проблеми з асинхронністю: Якщо контекст залежить від async‑запитів, треба обробляти loading/erro states.
- Проблеми з багатопоточністю: При використанні React 18 Concurrent Mode, Context‑значення можуть бути «запізнілими».
Cheatsheet
createContext(defaultValue)– створює контекст.<Provider value={...}>– огортає дерево.useContext(Context)– читає значення.- Для глобального state: Context +
useReducerабо зовнішній store.
Follow‑up питання
- Як уникнути непотрібних перерендерів при використанні Context? – Відповідь: мемоізувати value, використовувати
React.memo. - Коли краще використати Redux замість Context? – Відповідь: коли state змінюється часто і потрібна складна логіка.
- Як працює Context у SSR? – Відповідь: значення повинні бути синхронними, або використовувати
Hydration.