Как использовать useContext с Context API?react-43

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

1. Что такое useContext?

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

2. Связь с Context API

useContext является частью Context API и предоставляет более удобный способ потребления контекста по сравнению с традиционным Context.Consumer.


Базовый пример использования

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

import { createContext } from 'react';

// Создаем контекст с значением по умолчанию
const ThemeContext = createContext('light');

2. Создание провайдера

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <Toolbar />
    </ThemeContext.Provider>
  );
}

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

import { useContext } from 'react';

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

  return (
    <button style={{ background: theme === 'dark' ? '#333' : '#FFF' }}>
      Кнопка
    </button>
  );
}

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

1. Динамический контекст

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

  const toggleTheme = () => {
    setTheme(prev => prev === 'light' ? 'dark' : 'light');
  };

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

// Использование в компоненте
function ThemeToggler() {
  const { theme, toggleTheme } = useContext(ThemeContext);

  return (
    <button onClick={toggleTheme}>
      Текущая тема: {theme}
    </button>
  );
}

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

function useTheme() {
  const context = useContext(ThemeContext);
  if (!context) {
    throw new Error('useTheme must be used within a ThemeProvider');
  }
  return context;
}

// Теперь везде используем useTheme() вместо useContext(ThemeContext)

3. Множественные контексты

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

  return (
    <div className={theme}>
      <h1>{user.name}</h1>
    </div>
  );
}

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

1. Мемоизация значения контекста

function App() {
  const [user, setUser] = useState({ name: 'John', age: 30 });

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

  return (
    <UserContext.Provider value={userContextValue}>
      <Content />
    </UserContext.Provider>
  );
}

2. Разделение контекстов

// Вместо одного большого контекста
<UserContext.Provider value={{ user, preferences, settings }}>

// Лучше использовать несколько специализированных
<UserContext.Provider value={user}>
  <PreferencesContext.Provider value={preferences}>
    <SettingsContext.Provider value={settings}>

Обработка ошибок

1. Проверка существования контекста

function useTheme() {
  const context = useContext(ThemeContext);
  if (context === undefined) {
    throw new Error('useTheme must be used within a ThemeProvider');
  }
  return context;
}

2. Значение по умолчанию

const ThemeContext = createContext('light');

// Если Provider не будет найден выше по дереву,
// useContext(ThemeContext) вернет 'light'

Сравнение с Context.Consumer

Старый способ

function Button() {
  return (
    <ThemeContext.Consumer>
      {theme => (
        <button className={theme}>Кнопка</button>
      )}
    </ThemeContext.Consumer>
  );
}

Новый способ

function Button() {
  const theme = useContext(ThemeContext);
  return <button className={theme}>Кнопка</button>;
}

Лучшие практики

  1. Не злоупотребляйте контекстом: Используйте для действительно глобальных данных
  2. Создавайте кастомные хуки: Для лучшей инкапсуляции логики
  3. Разделяйте контексты: По функциональности (тема, пользователь, настройки)
  4. Мемоизируйте значения: Для предотвращения лишних ререндеров

Ограничения

  1. Не заменяет глобальное состояние: Для сложных случаев лучше Redux/MobX
  2. Нет встроенной оптимизации: В отличие от Redux connect
  3. Может вызывать лишние ререндеры: Если не мемоизировать значение

Резюмируем

  1. useContext упрощает потребление контекста в функциональных компонентах
  2. Всегда проверяйте наличие Provider'а через кастомные хуки
  3. Мемоизируйте значения контекста для оптимизации производительности
  4. Разделяйте большие контексты на специализированные
  5. Используйте кастомные хуки для лучшей инкапсуляции логики

Для большинства случаев использования глобального состояния в React-приложениях Context API с useContext - это отличное решение с минимальным бойлерплейтом.