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

Контекст (Context API) JS: React

Передача данных через пропсы вниз по иерархии компонентов — это немного многословный, но простой механизм. Всегда видно, откуда пришли данные и как они попали внутрь, а компоненты легко переиспользовать, так как они зависят только от входных данных. Но бывают ситуации, когда передача пропсов не вписывается в то, как работает код.

Возьмем для примера текущего пользователя. Часто данные пользователя нужны одновременно в разных частях страницы, причем в очень глубоких компонентах. Для этого придется передавать пользователя буквально по всей иерархии: даже там, где он не нужен компоненту. Единственная цель такой передачи — прокинуть данные до места назначения, пройдя по пути все промежуточные компоненты. Получается, что множество компонентов никак не используют пользователя, они просто передают их дальше по цепочке. В нашей ситуации данные пользователя глобальные. Они нужны сразу многим компонентам на разных уровнях иерархии. Для таких задач в React существует обходной путь.

Context API — механизм, позволяющий сделать глобальные данные доступными из любого компонента напрямую, без прокидывания пропсов. Его использование сводится к трем шагам:

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

    // В параметр передается значение по умолчанию
    // Здесь передаем пустой объект, потому что пользователя еще нет,
    // но он будет (и будет объектом)
    // Контекст может хранить только одно значение
    // Имя контекста выбирается исходя из того, какие внутри хранятся данные
    const UserContext = React.createContext({});
    
  2. Передача данных в контекст. Работает так: оборачиваем нужные компоненты в компонент контекста <UserContext.Provider> и передаем туда нужные данные в проп value:

    // Контекст будет доступен только внутри тех компонентов, которые он оборачивает
    // и в тех, что вложены в данные компоненты
    // currentUser — данные текущего пользователя
    <UserContext.Provider value={currentUser}>
      <App />
    </UserContext.Provider>
    
  3. Получение данных из контекста. В компоненте, где нужны данные, нужно указать тип контекста с помощью статического свойства contextType. Реакт ищет ближайший провайдер этого контекста и берет из него значение. Поиск провайдера происходит вверх по дереву компонентов. Значение контекста будет доступно в this.context:

    import UserContext from '...';
    
    // Любой компонент внутри блока <UserContext.Provider>
    class InnerComponent extends React.Component {
    
      // Определяем тип контекста
      static contextType = UserContext;
      render() {
        // Получаем доступ к контексту через this.context
        return <Profile user={this.context} />;
      }
    }
    

Еще один пример, где несколько компонентов используют данные из контекста:

// Создаем контекст
const CompanyContext = React.createContext({});

// Компонент адреса компании
class CompanyAddressComponent extends React.Component {
  // Компонент использует контекст
  static contextType = CompanyContext;

  render() {
    // Извлекаем данные из контекста
    const { context } = this;
    const { address } = context;
    return (
      <>
        {address.street}
         <br />
        {address.post} {address.city} {address.country}
      </>
    );
  }
}

// Другой компонент отрисовывает название компании
class CompanyNameComponent extends React.Component {
  // Оба компонента используют один контекст
  static contextType = CompanyContext;

  render() {
    const { context } = this;
    const { name } = context;
    return <>
      {name}
    </>;
  }
}

class App extends React.Component {
  render() {
    // Компоненты могут быть вложены на любой глубине
    return (
      <>
        <CompanyNameComponent />
        <br />
        <CompanyAddressComponent />
      </>
    );
  }
}

const company = {
  name: 'Hexlet Ltd.',
  address: {
    street: 'Itälahdenkatu 22 A',
    post: '00210',
    city: 'Helsinki',
    country: 'Finland',
  },
};

const dom = (
  <CompanyContext.Provider value={company}>
    <App />
  </CompanyContext.Provider>
);

const mountNode = document.getElementById('react-root');
const root = ReactDOM.createRoot(mountNode);
root.render(dom);

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


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

  1. Context API

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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