Як створити глобальний стан з useContext?
Що робить useContext
useContext дозволяє:
Створити "глобальний контейнер" даних (через
Context) Дати доступ до цих даних будь-якому компоненту в дереві Без передавання пропсів "вниз по ланцюжку" (prop drilling)
1. Створи контекст
import { createContext } from "react";
// Створюємо контекст
export const ThemeContext = createContext();Контекст - це "коробка", у якій React зберігатиме дані. Пізніше ти "заллєш" туди стан і "розіллєш" по застосунку.
2. Створи провайдер (компонент, де зберігається стан)
import { useState, createContext } from "react";
// створюємо контекст
export const ThemeContext = createContext();
export function ThemeProvider({ children }) {
const [theme, setTheme] = useState("light");
const toggleTheme = () =>
setTheme((prev) => (prev === "light" ? "dark" : "light"));
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}Що відбувається:
- Ми створили глобальний стан
theme - Обгорнули його в
<ThemeContext.Provider /> - Передали у
valueте, чим хочемо ділитися (theme,toggleTheme)
Тепер усі дочірні компоненти всередині <ThemeProvider> зможуть отримувати доступ до цих даних.
3. Обгорни весь застосунок провайдером
У App.jsx або main.jsx:
import { ThemeProvider } from "./ThemeContext";
import Page from "./Page";
function App() {
return (
<ThemeProvider>
<Page />
</ThemeProvider>
);
}
export default App;Тепер усі компоненти всередині ThemeProvider
зможуть використовувати useContext(ThemeContext)
і читати / змінювати глобальний стан.
4. Використай useContext у будь-якому компоненті
import { useContext } from "react";
import { ThemeContext } from "./ThemeContext";
function Header() {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<header
style={{
backgroundColor: theme === "light" ? "#fff" : "#333",
color: theme === "light" ? "#000" : "#fff",
}}
>
<p>Поточна тема: {theme}</p>
<button onClick={toggleTheme}>Змінити тему</button>
</header>
);
}Усе!
Компонент Header тепер отримує доступ до глобального стану теми,
хоча він зберігається "вгорі" застосунку, без передавання через пропси.
Як це працює під капотом
createContext()створює "канал зв'язку" між компонентами.<Provider value={...}>робить дані доступними для всіх нащадків.useContext(Context)підписує компонент на оновлення цього контексту. → Якщоvalueу провайдері змінюється, React перемалює всіх підписників.
Важливо: useContext не панацея
Хоча Context = "глобальний стан", використовувати його потрібно обережно:
| Проблема | Опис |
|---|---|
| Усі підписники ререндеряться | При кожній зміні значення у Provider |
| Не підходить для дуже часто змінюваних даних | Наприклад, лічильник FPS або курсор миші |
| Краще підходить для "налаштувань" | Тема, мова, поточний користувач, авторизація |
Для часто оновлюваних даних краще використовувати Zustand, Jotai, Redux, або розділяти контексти (щоб кожен містив лише одну частину стану).
Повний приклад структури проекту
src/
├─ context/
│ └─ ThemeContext.jsx
├─ components/
│ ├─ Header.jsx
│ └─ Footer.jsx
├─ App.jsx
└─ main.jsxThemeContext.jsx
import { createContext, useState } from "react";
export const ThemeContext = createContext();
export function ThemeProvider({ children }) {
const [theme, setTheme] = useState("light");
const toggleTheme = () =>
setTheme((prev) => (prev === "light" ? "dark" : "light"));
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}Header.jsx
import { useContext } from "react";
import { ThemeContext } from "../context/ThemeContext";
export function Header() {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<header className={theme}>
<h1>Тема: {theme}</h1>
<button onClick={toggleTheme}>Змінити</button>
</header>
);
}App.jsx
import { ThemeProvider } from "./context/ThemeContext";
import { Header } from "./components/Header";
export default function App() {
return (
<ThemeProvider>
<Header />
</ThemeProvider>
);
}Підсумок
| Питання | Відповідь |
|---|---|
Що робить useContext? | Дозволяє читати дані з найближчого <Context.Provider> |
| Як створити глобальний стан? | Обгорни стан у Context.Provider і використовуй useContext |
| Коли застосовувати? | Коли дані потрібні в різних місцях дерева (тема, мова, користувач) |
| Чим замінити у складних випадках? | Zustand, Redux, Jotai, Recoil та ін. |
| Як уникнути зайвих ререндерів? | Розділяй контексти й мемоізуй value (через useMemo) |
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.