Как использовать useContext() в React?
Контекст React
предоставляет данные компонентам независимо от того, насколько глубоко они находятся в дереве компонентов. Контекст используется для управления глобальными данными, например глобальным состоянием, темой, пользовательскими настройками и многим другим.
В этой статье вы узнаете, как использовать концепцию контекста в React
.
Как использовать контекст?
Для использования контекста в React требуется 3 простых шага: создание контекста, предоставление контекста и использование контекста.
1. Создание контекста
Встроенная функция createContext(default)
создает экземпляр контекста:
import { createContext } from 'react'; const Context = createContext('Default Value');
Функция фабрика принимает один необязательный аргумент: значение по умолчанию.
2. Предоставление контекста
Компонент Context.Provider
доступный в экземпляре контекста, используется для предоставления контекста его дочерним компонентам, независимо от того, насколько они глубоко расположены в дереве компонентов.
Чтобы задать значение контекста, используйте value
prop, доступное в <Context.Provider value={value} />
:
const Main = () => { const value = 'My Context Value'; return ( <Context.Provider value={value}> <MyComponent /> </Context.Provider> ); }
Опять же, здесь важно то, что все компоненты, которые хотели бы позже использовать контекст, должны быть обернуты внутри компонента поставщика.
Если вы хотите изменить значение контекста, просто обновите value
.
3. Использование контекста
Использование контекста может быть выполнено 2 способами.
Первый способ, который я рекомендую - это использовать React хук useContext(Context)
:
import { useContext } from 'react'; const MyComponent = () => { const value = useContext(Context); return <span>{value}</span>; }
Хук возвращает значение контекста:value = useContext(Context)
. Хук также гарантирует повторную перерисовку компонента при изменении значения контекста.
Второй способ заключается в использовании функции рендеринга, предоставляемой в качестве дочернего элемента Context.Consumer
- специальный компонент, доступный в экземпляре контекста:
const MyComponent = () => ( <Context.Consumer> {value => <span>{value}</span>} </Context.Consumer> );
Опять же, в случае изменения значения контекста, <Context.Consumer>
повторно вызовет свою функцию рендеринга.
У вас может быть столько потребителей, сколько вы хотите для одного контекста. Если значение контекста изменяется (путем изменения value
prop поставщика <Context.Provider value={value} />
), то все потребители немедленно уведомляются и повторно перерисовываются.
Если потребитель не обернут внутри поставщика, но все еще пытается получить доступ к значению контекста (используя useContext(Context)
или <Context.Consumer>
), тогда значение контекста будет аргументом значения по умолчанию, предоставленным заводской функции createContext(defaultValue)
, которая создала контекст.
Когда нужен контекст?
Основная идея использования контекста состоит в том, чтобы позволить вашим компонентам получать доступ к некоторым глобальным данным и повторно отображать их при изменении этих глобальных данных. Контекст решает проблему цепочки прокидывания пропсов: когда вам нужно передавать пропсы от родителей к детям.
Вы можете хранить внутри контекста:
- глобальное состояние
- тему
- конфигурацию приложения
- аутентифицированное имя
- пользователя настройки
- пользователя предпочитаемый язык
- набор служб
С другой стороны, вам следует тщательно подумать, прежде чем принимать решение об использовании контекста в вашем приложении.
Во-первых, интеграция контекста добавляет сложности. Создание контекста, перенос данных в поставщика, использование useContext()
в каждом потребителе — это увеличивает сложность.
Во-вторых, добавление контекста затрудняет модульное тестирование компонентов. Во время модульного тестирования вам придется обернуть потребительские компоненты в поставщика контекста. Включая компоненты, на которые косвенно влияет контекст — предки потребителей контекста!
Пример использования: глобальное имя пользователя
Самый простой способ передачи данных от родительского к дочернему компоненту - это когда родительский компонент присваивает реквизиты своему дочернему компоненту:
const Application = () => { const userName = "John Smith"; return <UserInfo userName={userName} />; } const UserInfo = ({ userName }) => <span>{userName}</span>;
Родительский компонент <Application />
присваивает данные имени пользователя своему дочернему компоненту <UserInfo name={userName} />
, используя проп userName
.
Это обычный способ передачи данных с использованием пропсов. Вы можете использовать этот подход без проблем.
Ситуация меняется, когда дочерний компонент <UserInfo />
не является прямым дочерним компонентом <Application />
, а содержится в нескольких предках.
Например, предположим, что компонент <Application />
(тот, у которого есть имя пользователя глобальных данных) отображает компонент <Layout />
, который, в свою очередь, отображает компонент <Header />
, который, в свою очередь, в конечном итоге отображает компонент <UserInfo />
(который хотел бы получить доступ к имени пользователя).
Вот как будет выглядеть такая структура:
const Application = () => { const userName = "John Smith"; return ( <Layout userName={userName}> Main content </Layout> ); }; const Layout = ({ children, userName }) => ( <div> <Header userName={userName} /> <main> {children} </main> </div> ); const Header = ({ userName }) => ( <header> <UserInfo userName={userName} /> </header> ); const UserInfo = ({ userName }) => <span>{userName}</span>;
Вы можете видеть проблему: потому что компонент <UserInfo />
отображается глубоко в дереве, и все родительские компоненты (<Layout />
и <Header />
) должны передавать проп имени пользователя.
Эта проблема известна как сквозная передача данных.
Контекст - это возможное решение. Давайте посмотрим, как его применить.
Контекст для спасения
В качестве краткого напоминания, для применения контекста React требуются 3 участника: контекст, поставщик и потребитель.
Вот как будет выглядеть пример приложения при применении к нему контекста:
import { useContext, createContext } from 'react'; const UserContext = createContext('Unknown'); const Application = () => { const userName = "John Smith"; return ( <UserContext.Provider value={userName}> <Layout> Main content </Layout> </UserContext.Provider> ); } const Layout = ({ children }) => ( <div> <Header /> <main> {children} </main> </div> ); const Header = () => ( <header> <UserInfo /> </header> ); const UserInfo = () => { const userName = useContext(UserContext); return <span>{userName}</span>; }
Давайте более подробно рассмотрим, что было сделано.
Во-первых, const UserContext = createContext('Unknown')
создает контекст, который будет содержать информацию об имени пользователя.
Во-вторых, внутри компонента <Application />
дочерние компоненты приложения заключены в поставщика контекста: <UserContext.Provider value={userName}>
. Обратите внимание, что значение prop
компонента provider
имеет важное значение: именно так вы устанавливаете значение контекста.
И в итоге <UserInfo /> становится потребителем контекста с помощью встроенного хука useContext(UserContext)
. Хук вызывается с контекстом в качестве аргумента и возвращает значение имени пользователя.
Промежуточные компоненты <Layout />
и <Header />
не должны передавать пропс с именем пользователя. Это большое преимущество контекста: он снимает бремя передачи данных через промежуточные компоненты.
Когда контекст меняется
Когда значение контекста изменяется путем изменения значения prop поставщика контекста (<Context.Provider value={value} />
), то все его потребители получают уведомление и повторно перерисовываются.
Например, если я изменю имя пользователя с 'John Smith'
на 'Smith, John Smith'
, то <UserInfo />
немедленно повторно перерисуется с последним значеним контекста.
Итоги
Контекст в React - это концепция, которая позволяет вам предоставлять дочерним компонентам глобальные данные, независимо от того, насколько глубоко они находятся в дереве компонентов.
Использование контекста требует 3 шагов: создание, предоставление и использование контекста.
При интеграции контекста в ваше приложение учитывайте, что это добавляет значительную сложность. Иногда сквозная передача данных через 2-3 уровня в иерархии компонентов не является большой проблемой.
Не забывайте подписываться на телеграм канал, чтобы не пропустить новые статьи, до встречи ✌️
Читайте еще:
- Что такое сборщики JavaScript и зачем их использовать
- Возможно, вы не все знаете о useState...
- React Hook для бесконечной прокрутки