Передача данных через пропсы вниз по иерархии компонентов — это немного многословный, но простой механизм. Всегда видно, откуда пришли данные и как они попали внутрь, а компоненты легко переиспользовать, так как они зависят только от входных данных. Но бывают ситуации, когда передача пропсов не вписывается в то, как работает код.
Возьмем для примера текущего пользователя. Часто данные пользователя нужны одновременно в разных частях страницы, причем в очень глубоких компонентах. Для этого придется передавать пользователя буквально по всей иерархии: даже там, где он не нужен компоненту. Единственная цель такой передачи — прокинуть данные до места назначения, пройдя по пути все промежуточные компоненты. Получается, что множество компонентов никак не используют пользователя, они просто передают их дальше по цепочке. В нашей ситуации данные пользователя глобальные. Они нужны сразу многим компонентам на разных уровнях иерархии. Для таких задач в React существует обходной путь.
Context API — механизм, позволяющий сделать глобальные данные доступными из любого компонента напрямую, без прокидывания пропсов. Его использование сводится к трем шагам:
Создание контекста:
// В параметр передается значение по умолчанию // Здесь передаем пустой объект, потому что пользователя еще нет, // но он будет (и будет объектом) // Контекст может хранить только одно значение // Имя контекста выбирается исходя из того, какие внутри хранятся данные const UserContext = React.createContext({});
Передача данных в контекст. Работает так: оборачиваем нужные компоненты в компонент контекста
<UserContext.Provider>
и передаем туда нужные данные в пропvalue
:// Контекст будет доступен только внутри тех компонентов, которые он оборачивает // и в тех, что вложены в данные компоненты // currentUser — данные текущего пользователя <UserContext.Provider value={currentUser}> <App /> </UserContext.Provider>
Получение данных из контекста. В компоненте, где нужны данные, нужно указать тип контекста с помощью статического свойства
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);
В отличие от пропсов, изменение данных в контексте не приводит к перерисовке по умолчанию. Идеально, когда данные в контексте используются только для чтения. Изменяемые данные лучше хранить внутри состояния компонентов. Однако, если очень нужно, то реагировать на изменение контекста возможно, об этом подробнее можно прочитать в документации. В прикладном коде такая возможность используется редко, но на ней основаны разнообразные библиотеки.
Дополнительные материалы
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
- Статья «Как учиться и справляться с негативными мыслями»
- Статья «Ловушки обучения»
- Статья «Сложные простые задачи по программированию»
- Вебинар «Как самостоятельно учиться»
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.