Что такое useContext и как его использовать?react-21

useContext — это хук (hook) в React, который позволяет подписаться на контекст (context) без использования компонента-потребителя (Consumer). Это упрощает доступ к глобальным данным в дереве компонентов.

Основные понятия

  1. Контекст (Context) — механизм передачи данных через дерево компонентов без явной передачи пропсов
  2. Провайдер (Provider) — компонент, который предоставляет значение контекста
  3. Потребитель (Consumer) — компонент, который потребляет значение контекста (заменяется на useContext)

Базовый синтаксис

const value = useContext(MyContext);

Полный цикл работы с контекстом

1. Создание контекста

import { createContext } from 'react';

const ThemeContext = createContext('light'); // Значение по умолчанию

2. Предоставление контекста

function App() {
  const [theme, setTheme] = useState('dark');

  return (
    <ThemeContext.Provider value={theme}>
      <Toolbar />
      <button onClick={() => setTheme(prev => prev === 'dark' ? 'light' : 'dark')}>
        Сменить тему
      </button>
    </ThemeContext.Provider>
  );
}

3. Потребление контекста

function ThemedButton() {
  const theme = useContext(ThemeContext);

  return (
    <button style={{
      background: theme === 'dark' ? '#333' : '#EEE',
      color: theme === 'dark' ? '#FFF' : '#000'
    }}>
      Кнопка с темой {theme}
    </button>
  );
}

Практические примеры

Пример с пользователем

// Создаем контекст
const UserContext = createContext(null);

// Компонент-провайдер
function App() {
  const [user, setUser] = useState({ name: 'Анна', role: 'admin' });

  return (
    <UserContext.Provider value={{ user, setUser }}>
      <Navbar />
      <Profile />
    </UserContext.Provider>
  );
}

// Компонент-потребитель
function Profile() {
  const { user } = useContext(UserContext);

  return <div>Привет, {user.name}!</div>;
}

Пример с мультиконтекстом

function Header() {
  const theme = useContext(ThemeContext);
  const user = useContext(UserContext);

  return (
    <header style={{ background: theme === 'dark' ? '#222' : '#DDD' }}>
      Добро пожаловать, {user.name}!
    </header>
  );
}

Оптимизация производительности

  1. Мемоизация значения контекста:
function App() {
  const [user, setUser] = useState({ name: 'Анна' });

  // Мемоизированное значение контекста
  const userValue = useMemo(() => ({ user, setUser }), [user]);

  return (
    <UserContext.Provider value={userValue}>
      <Content />
    </UserContext.Provider>
  );
}
  1. Разделение контекстов для часто изменяющихся значений

Распространенные ошибки

  1. Использование useContext без Provider:

    • Получите значение по умолчанию из createContext
    • Убедитесь, что компонент обернут в Provider
  2. Ненужное использование контекста:

    • Для простых случаев лучше использовать пропсы
    • Контекст предназначен для действительно глобальных данных
  3. Забывают мемоизировать значение:

    // Плохо: новый объект при каждом рендере
    <UserContext.Provider value={{ user, setUser }}>
    
    // Хорошо: мемоизированное значение
    const value = useMemo(() => ({ user, setUser }), [user]);
    <UserContext.Provider value={value}>
    

Продвинутые паттерны

Кастомный хук для контекста

function useUser() {
  const context = useContext(UserContext);
  if (!context) {
    throw new Error('useUser must be used within a UserProvider');
  }
  return context;
}

// Использование
const { user } = useUser();

Контекст с редюсером

const UserDispatch = createContext(null);

function App() {
  const [user, dispatch] = useReducer(userReducer, initialUser);

  return (
    <UserDispatch.Provider value={dispatch}>
      <UserState.Provider value={user}>
        <Content />
      </UserState.Provider>
    </UserDispatch.Provider>
  );
}

// В компоненте
function UpdateButton() {
  const dispatch = useContext(UserDispatch);

  return (
    <button onClick={() => dispatch({ type: 'UPDATE_NAME', name: 'Новое имя' })}>
      Обновить
    </button>
  );
}

Резюмируем

useContext — это мощный инструмент для управления глобальным состоянием приложения, который устраняет необходимость в пропс-дриллинге (prop drilling). Он предоставляет простой и элегантный способ доступа к данным контекста в функциональных компонентах. При правильном использовании с мемоизацией значений и разделением контекстов, он становится эффективным решением для управления состоянием приложения. Однако важно не злоупотреблять им и использовать только для действительно глобальных данных, которые нужны многим компонентам в разных частях приложения.