Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером

Хук useContext JS: React Hooks

React передает данные внутрь компонентов с верхних уровней на нижние, используя пропсы.

Такой подход неудобен при работе с глобальными данными, которые нужны одновременно во многих компонентах на разных уровнях иерархии. К таким данным относится текущий уровень, текущая тема (темная или светлая) и так далее. Напрямую передавать такие данные неудобно, придется протаскивать их сквозь все приложение.

В таких случаях React позволяет передать данные в приложение и получить внутри любого компонента доступ к этим данным напрямую, минуя пропсы. Этот механизм называется контекст.

Хук useContext() позволяет использовать контекст внутри компонента. Для этого нужно выполнить три действия:

  1. Инициализировать контекст в том же месте, где инициализируется приложение

    // Параметром передается значение по умолчанию
    // Имя контекста выбирается произвольно
    const UserContext = React.createContext({});
    
  2. Подключить провайдер и передать данные в контекст через пропс value.

    // user — данные которые лежат внутри контекста
    <UserContext.Provider value={user}>
      <MyComponent />
    </UserContext.Provider>
    
  3. Получить данные контекста

    import React, { useContext } from 'react';
    
    const MyComponent = () => {
      // Возвращает контекст целиком
      const user = useContext(UserContext);
    
      return <h1>{user.name}</h1>;
    }
    

Вот другой пример контекста, в котором хранится текущая тема:

const themes = {
  light: {
    foreground: '#000000',
    background: '#eeeeee',
  },
  dark: {
    foreground: '#ffffff',
    background: '#222222',
  },
};

const ThemeContext = React.createContext({});

И где-то внутри приложения:

<ThemeContext.Provider value={/* текущая тема */}>
  <Content />
</ThemeContext.Provider>

Метод React.createContext() принимает значение по умолчанию. Это значение будет передаваться в контекст тех компонентов, которые не обернуты в провайдер. Обычно провайдер всегда используется, чтобы оборачивать все приложение. Поэтому компоненты всегда принимают в контексте значение, переданное провайдером. Но если мы работаем с компонентом вне провайдера, может понадобиться такое значение по умолчанию. Например, такая ситуация может сложиться при тестировании компонента отдельно от приложения.

Внутри контекста может храниться как примитивное значение, так и объект. Изменение содержимого такого объекта никак не отслеживается React, поэтому не приводит к перерендеру. Но если заменить сам объект, то из-за изменившейся ссылки React узнает об изменении и выполнит перерисовку компонентов внутри провайдера.

Иногда возникает ситуация, когда в контексте нужно хранить динамические данные. Например, при авторизации. Когда пользователь авторизован, мы должны сохранить какие-то данные, чтобы пользователю предоставлялись дополнительные функции. Сам по себе контекст для этого ничего не предоставляет, но можно передать в контекст методы для манипулирования данными, а сами данные хранить с помощью useState(). Более продвинутый вариант — это создать провайдер в отдельном компоненте. Так мы сможем изолировать данные от всего приложения, а в компоненты передавать интерфейс для взаимодействия с данными.

Для этого создадим контекст в отдельном модуле, чтобы все компоненты могли его импортировать:

// ThemeContext.js

import { createContext } from 'react';

// Создаём контекст и задаем значения по умолчанию для него
export default createContext({
  themes: {},
  theme: {},
  setTheme: () => {},
});

Создаем отдельный компонент провайдера:

// ThemeProvider.jsx

import ThemeContext from './ThemeContext.js';

const themes = {
  light: {
    foreground: '#000000',
    background: '#eeeeee',
  },
  dark: {
    foreground: '#ffffff',
    background: '#222222',
  },
};

const ThemeProvider = ({ children }) => {
  const [theme, setTheme] = useState(themes.dark);

  const setLightTheme = () => setTheme(themes.light);

  const setDarkTheme = () => setTheme(themes.dark);

  // Передаем данные и функции в контекст провайдера
  return (
    <ThemeContext.Provider value={{ theme, setLightTheme, setDarkTheme, themes }}>
      {children}
    </ThemeContext.Provider>
  );
};

export default ThemeProvider;

И внутри приложения используем провайдер как обычный компонент:

// App.jsx

import React from 'react';
import ThemeProvider from './ThemeProvider.jsx';
import MyComponent from './MyComponent.jsx';

// Используем провайдер для доступа к контексту внутри всего приложения
const App = () => {
  return (
    <ThemeProvider>
      <MyComponent />
    </ThemeProvider>
  );
};

export default App;

В самом компоненте теперь можно импортировать контекст и использовать функции из провайдера для изменения состояния в контексте:

// MyComponent.jsx

import React, { useContext } from 'react';
import ThemeContext from './ThemeContext.js';

const MyComponent = () => {
    // Получаем доступ к контексту
  const { setLightTheme } = useContext(ThemeContext);

  return <button onClick={() => setLightTheme()}>Включить светлую тему</button>;
}

export default MyComponent;

Не забываем встроить приложение на страницу:

// Входной файл index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.js';

ReactDOM.render(<App />, document.getElementById('root'));

Контекст - удобный механизм для некоторых особых ситуаций, но он не должен становиться основным способом передачи данных внутрь приложения. Такой соблазн появляется у многих, кто использует его впервые. Главная проблема контекстов — связывание компонентов с глобальными данными, а это затрудняет их повторное использование в других ситуациях.


Дополнительные материалы

  1. Использование хука useContext

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Об обучении на Хекслете

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы
профессия
от 6 300 ₽ в месяц
Разработка фронтенд-компонентов для веб-приложений
10 месяцев
с нуля
Старт 25 апреля
профессия
от 9 900 ₽ в месяц
Разработка фронтенд- и бэкенд-компонентов для веб-приложений
16 месяцев
с нуля
Старт 25 апреля

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»