Руководство по React Context и хуку useContext()

Руководство по React Context и хуку useContext()

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

1. Как использовать контекст

Для использования контекста в React требуется 3 простых шага: создание контекста, предоставление контекста и использование контекста.

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

Встроенная функция createContext(default) создает экземпляр контекста:

Функция принимает один необязательный аргумент: значение по умолчанию.

Б. Предоставление контекста

Компонент сontext.Provider, доступный в экземпляре контекста, используется для предоставления контекста его дочерним компонентам, независимо от того, насколько они глубоки. Чтобы установить значение контекста, используйте свойство value, доступное через <Context.Provider value={value} />:

Если вы хотите изменить значение контекста, просто обновите свойство value.

C. Использование контекста

Использование контекста может быть выполнено двумя способами. Первый способ, который я рекомендую, — это использовать React хук useContext(Context):

Попробуйте демо.

Хук возвращает значение контекста: value = useContext(Context). Также он гарантирует повторный рендеринг компонента при изменении значения контекста.

Второй способ — использовать функцию рендеринга, предоставленную в качестве дочернего компонента Context.Consumer, доступного в экземпляре контекста:

Попробуйте демо.

Опять же, в случае, если значение контекста изменится, функция рендеринга <Context.Consumer> будет повторно визуализирована.

Руководство по React Context и хуку useContext()

Вы можете иметь столько потребителей, сколько хотите для одного контекста. Если значение контекста изменяется (путем изменения свойства value в <Context.Provider value={value} />), все потребители немедленно уведомляются и повторно обрабатываются.

Если потребитель не заключен внутри провайдера, но все же пытается получить доступ к значению контекста (используя useContext(Context) или <Context.Consumer>), тогда значение контекста будет аргументом значения по умолчанию, предоставленным функции createContext(defaultValue), которая создала контекст.

2. Когда вам нужен контекст?

Основная идея использования контекста — предоставить вашим компонентам доступ к некоторым глобальным данным и повторный рендеринг при изменении этих данных. Контекст решает проблему когда вам нужно передать свойства от родителей потомкам.

Вы можете держать внутри контекста:

глобальное состояние

тему

конфигурацию приложения

аутентифицированное имя пользователя

пользовательские настройки

предпочтительный язык

набор услуг

С другой стороны, вы должны хорошо подумать, прежде чем принимать решение об использовании контекста в своем приложении.

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

Во-вторых, добавление контекста затрудняет модульное тестирование компонентов. Во время модульного тестирования вам придется заключить потребительские компоненты в поставщик контекста, включая компоненты, на которые косвенно влияет контекст — предков потребителей контекста!

3. Пример использования: глобальное имя пользователя.

Самый простой способ передать данные от родителя к дочернему компоненту — это когда родитель присваивает свойства своему дочернему компоненту:

Родительский компонент <Application /> присваивает данные userName своему дочернему компоненту <UserInfo name={userName} /> с помощью свойства userName.

Это обычный способ передачи данных с использованием свойств. Вы можете использовать этот подход без проблем.
Ситуация меняется, когда дочерний компонент <UserInfo /> не является прямым потомком <Application />, но содержится в нескольких предках.

Например, предположим, что компонент <Application /> (тот, у которого есть глобальные данные userName) визуализирует компонент <Layout />, который, в свою очередь, визуализирует компонент <Header />, который, в свою очередь, наконец, визуализирует компонент <UserInfo /> (который хотел бы получить доступ к userName). Вот как будет выглядеть такое структурирование:

Попробуйте демо.

Вы можете заметить проблему: компонент <UserInfo /> отображается глубоко в дереве, и все родительские компоненты (<Layout /> и <Header />) должны передавать свойство userName. Эта проблема также известна как дриллинг свойств.

Контекст React — возможное решение. Давайте посмотрим, как его применить в следующем разделе.

3.1 Контекст в помощь

Напоминаем, что для применения контекста React требуются 3 участника: контекст, поставщик, извлеченный из контекста, и потребитель. Вот как будет выглядеть пример приложения при применении к нему контекста:

Попробуйте демо.

Разберемся подробнее, что было сделано. Сначала создается контекст const UserContext = createContext(‘Unknown’), в котором будет храниться информация об имени пользователя.

Потом, внутри компонента <Application />, дочерние компоненты помещаются внутри провайдера контекста пользователя: <UserContext.Provider value={userName}>. Обратите внимание на свойство value компонента поставщика: именно так вы устанавливаете значение контекста.

Наконец, <UserInfo /> становится потребителем контекста с помощью встроенного хука useContext(UserContext). Хук вызывается с контекстом в качестве аргумента и возвращает значение имени пользователя.

Промежуточные компоненты <Layout /> и <Header /> не должны передаваться свойством userName. В этом большое преимущество контекста: он снимает бремя передачи данных через промежуточные компоненты.

3.2 При изменении контекста

Когда значение контекста изменяется путем изменения свойства value поставщика контекста (<Context.Provider value={value} />), все его потребители уведомляются и повторно обрабатываются.

Например, если я изменю имя пользователя с ‘John Smith’ на ‘Smith, John Smith’, то потребитель <UserInfo /> немедленно выполнит повторный рендеринг, чтобы отобразить последнее значение контекста:

Откройте демонстрацию, и вы увидите ‘John Smith’ (значение контекста), отображаемое на экране. Через 2 секунды значение контекста изменится на ‘Smith, John Smith’, и, соответственно, новое значение будет показано на экране.

Демонстрация показывает, что компонент потребитель <UserInfo />, отображает значение контекста на экране, при изменении значения контекста повторно отображает его значение.

4. Обновление контекста

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

Но это может быть легко реализовано путем интеграции механизма управления состоянием (подобно хукам useState() и useReducer()), и обеспечения функции обновления прямо в контексте рядом с самим значением. В следующем примере компонент <Application /> использует хук useState() для управления значением контекста.

Попробуйте демо.

Потребитель <UserNameInput /> читает значение контекста, откуда извлекаются userName и setUserName. Затем потребитель может обновить значение контекста, вызвав функцию обновления setUserName(newContextValue).
<UserInfo /> — еще один потребитель контекста. Когда обновляет контекст — обновляется и компонент <UserInfo />.

Обратите внимание, что <Application /> запоминает значение контекста. Объект значения контекста сохраняется неизменным до тех пор, пока userName остается неизменным, предотвращая повторную визуализацию потребителей каждый раз при повторной визуализации <Application />.

В противном случае во время повторного рендеринга будут созданы разные экземпляры объекта, что вызовет повторный рендеринг <Application /> в потребителях контекста.

5. Вывод

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

Для использования контекста требуется 3 шага: создание, предоставление и использование контекста. При интеграции контекста в ваше приложение учтите, что иногда проще передать свойство через 2–3 уровня иерархии чем использовать контекст, потому что он значительно усложняет работу.

Автор: Dmitri Pavlutin

Источник: dmitripavlutin.com

Редакция: Команда webformyself.

Читайте нас в Telegram, VK, Яндекс.Дзен

Метки:

Похожие статьи:

Комментарии Вконтакте:

Комментарии запрещены.