← Назад к вопросам

Для чего используется Context API?

2.0 Middle🔥 251 комментариев
#React#State Management

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Context API в React: назначение и применение

Context API — это встроенный в React механизм для передачи данных через дерево компонентов без необходимости передавать props на каждом уровне. Это решает проблему "prop drilling" (передача props через множество промежуточных компонентов).

Проблема: Prop Drilling

Представь ситуацию, когда нужно передать данные с верхнего уровня на нижний:

// ❌ Плохо: свойства передаются через все промежуточные компоненты
function App() {
  const user = { name: "John", role: "admin" };
  return <Layout user={user} />;
}

function Layout({ user }) {
  return <Sidebar user={user} />;
}

function Sidebar({ user }) {
  return <Menu user={user} />;
}

function Menu({ user }) {
  return <UserInfo user={user} />;
}

function UserInfo({ user }) {
  return <div>{user.name} ({user.role})</div>;
}

В этом примере user передаётся через 4 компонента, которые не используют эти данные напрямую.

Решение: Context API

// ✅ Хорошо: данные доступны через Context
import { createContext, useContext } from "react";

// 1. Создаём Context
const UserContext = createContext();

// 2. Создаём Provider для предоставления данных
export function UserProvider({ children }) {
  const user = { name: "John", role: "admin" };
  
  return (
    <UserContext.Provider value={user}>
      {children}
    </UserContext.Provider>
  );
}

// 3. Создаём хук для удобного доступа
export function useUser() {
  return useContext(UserContext);
}

// 4. Используем
function App() {
  return (
    <UserProvider>
      <Layout />
    </UserProvider>
  );
}

function Layout() {
  return <Sidebar />;
}

function Sidebar() {
  return <Menu />;
}

function Menu() {
  return <UserInfo />;
}

function UserInfo() {
  const user = useUser(); // Получаем данные напрямую!
  return <div>{user.name} ({user.role})</div>;
}

Основные применения Context API

1. Тема приложения (Light/Dark Mode)

const ThemeContext = createContext();

export function ThemeProvider({ children }) {
  const [theme, setTheme] = useState(() => {
    // Загружаем из localStorage
    return localStorage.getItem("theme") || "light";
  });

  const toggleTheme = () => {
    const newTheme = theme === "light" ? "dark" : "light";
    setTheme(newTheme);
    localStorage.setItem("theme", newTheme);
  };

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

export function useTheme() {
  return useContext(ThemeContext);
}

// Использование в компонентах
function Header() {
  const { theme, toggleTheme } = useTheme();
  
  return (
    <header className={theme === "dark" ? "bg-gray-900" : "bg-white"}>
      <button onClick={toggleTheme}>
        Switch to {theme === "light" ? "dark" : "light"} mode
      </button>
    </header>
  );
}

2. Авторизация и пользовательские данные

const AuthContext = createContext();

export function AuthProvider({ children }) {
  const [user, setUser] = useState(null);
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    // Проверяем авторизацию при загрузке приложения
    checkAuth().then(userData => {
      setUser(userData);
      setIsLoading(false);
    }).catch(() => setIsLoading(false));
  }, []);

  const login = async (email, password) => {
    const userData = await api.login(email, password);
    setUser(userData);
    localStorage.setItem("token", userData.token);
  };

  const logout = () => {
    setUser(null);
    localStorage.removeItem("token");
  };

  return (
    <AuthContext.Provider value={{ user, isLoading, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
}

export function useAuth() {
  return useContext(AuthContext);
}

// Защищённый маршрут
function ProtectedRoute({ children }) {
  const { user, isLoading } = useAuth();

  if (isLoading) return <Spinner />;
  if (!user) return <Navigate to="/login" />;

  return children;
}

3. Языковые настройки (i18n)

const LanguageContext = createContext();

const translations = {
  en: {
    hello: "Hello",
    goodbye: "Goodbye",
  },
  ru: {
    hello: "Привет",
    goodbye: "До свидания",
  },
};

export function LanguageProvider({ children }) {
  const [language, setLanguage] = useState("en");

  const changeLanguage = (lang) => {
    setLanguage(lang);
    localStorage.setItem("language", lang);
  };

  const t = (key) => translations[language][key];

  return (
    <LanguageContext.Provider value={{ language, changeLanguage, t }}>
      {children}
    </LanguageContext.Provider>
  );
}

export function useLanguage() {
  return useContext(LanguageContext);
}

function Greeting() {
  const { t } = useLanguage();
  return <h1>{t("hello")}</h1>;
}

4. Модальные окна и уведомления

const NotificationContext = createContext();

export function NotificationProvider({ children }) {
  const [notifications, setNotifications] = useState([]);

  const addNotification = (message, type = "info") => {
    const id = Date.now();
    setNotifications(prev => [...prev, { id, message, type }]);

    // Автоматически удаляем через 3 секунды
    setTimeout(() => {
      removeNotification(id);
    }, 3000);
  };

  const removeNotification = (id) => {
    setNotifications(prev => prev.filter(n => n.id !== id));
  };

  return (
    <NotificationContext.Provider value={{ addNotification, removeNotification }}>
      {children}
      <NotificationContainer notifications={notifications} />
    </NotificationContext.Provider>
  );
}

export function useNotification() {
  const context = useContext(NotificationContext);
  return {
    success: (msg) => context.addNotification(msg, "success"),
    error: (msg) => context.addNotification(msg, "error"),
    info: (msg) => context.addNotification(msg, "info"),
  };
}

// Использование
function Form() {
  const { success, error } = useNotification();

  const handleSubmit = async (data) => {
    try {
      await api.save(data);
      success("Data saved successfully!");
    } catch (err) {
      error("Failed to save data");
    }
  };

  return <form onSubmit={handleSubmit}>{/* ... */}</form>;
}

5. Глобальные настройки приложения

const AppConfigContext = createContext();

export function AppConfigProvider({ children }) {
  const [config, setConfig] = useState({
    apiBaseUrl: process.env.REACT_APP_API_URL,
    enableAnalytics: true,
    pageSize: 20,
    maxRetries: 3,
  });

  const updateConfig = (newConfig) => {
    setConfig(prev => ({ ...prev, ...newConfig }));
  };

  return (
    <AppConfigContext.Provider value={{ config, updateConfig }}>
      {children}
    </AppConfigContext.Provider>
  );
}

export function useAppConfig() {
  return useContext(AppConfigContext);
}

Когда использовать Context API?

Context API хорошо подходит для:

  • Частые изменения: авторизация, тема, язык
  • Глубокие деревья компонентов: когда данные нужны на разных уровнях
  • Небольшие проекты: для быстрого прототипирования
  • Данные, которые редко меняются: конфигурация приложения

Когда НЕ использовать?

Не используй Context API, если:

  • Данные часто меняются (например, реальтайм данные) — используй Redux/Zustand
  • Нужен сложный state management — используй Redux Toolkit
  • Нужна временная шкала (devtools) — используй Redux
  • Нужна нормализация данных — используй Redux
// ❌ Плохо: Context для часто меняющихся данных
const HeavyUpdatesContext = createContext();

function Provider({ children }) {
  const [data, setData] = useState({});

  setInterval(() => {
    setData(prev => ({ ...prev, timestamp: Date.now() })); // Обновляется каждую секунду
  }, 1000);

  return (
    <HeavyUpdatesContext.Provider value={data}>
      {children}
    </HeavyUpdatesContext.Provider>
  );
}
// Все компоненты будут перерендеривать на каждое обновление!

Оптимизация Context

Чтобы избежать ненужных перерендеров, разделяй Context на части:

// ✅ Хорошо: отделяем статичные значения от часто меняющихся
const AuthStateContext = createContext(); // Реже меняется
const AuthActionsContext = createContext(); // Функции (стабильны)

function AuthProvider({ children }) {
  const [user, setUser] = useState(null);

  const value = { user }; // Объект для состояния

  const actions = {
    login: async (email, password) => {/* ... */},
    logout: () => {/* ... */},
  };

  return (
    <AuthStateContext.Provider value={value}>
      <AuthActionsContext.Provider value={actions}>
        {children}
      </AuthActionsContext.Provider>
    </AuthStateContext.Provider>
  );
}

// Компоненты подписываются только на то, что нужно
function LoginForm() {
  const { login } = useContext(AuthActionsContext); // Не перерендерится при изменении user
  return <form onSubmit={() => login()}>...</form>;
}

Заключение

Context API — это мощный инструмент для избежания prop drilling и управления глобальным состоянием. Используй его для:

  • Авторизации
  • Темы
  • Языковых настроек
  • Уведомлений
  • Конфигурации приложения

Для сложного state management (часто меняющиеся данные, нормализация, временная шкала) используй Redux или Zustand.