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

0 0

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

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

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

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

JavaScript import { createContext } from ‘react’; const Context = createContext(‘Default Value’);

12import { createContext } from ‘react’;const Context = createContext(‘Default Value’);

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

React JS. Основы

Изучите основы ReactJS на практическом примере по созданию учебного веб-приложения

Получить курс сейчас!

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

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

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

JavaScript function Main() { const value = ‘My Context Value’; return ( <Context.Provider value={value}> <MyComponent /> </Context.Provider> ); }

12345678function Main() {  const value = ‘My Context Value’;  return (    <Context.Provider value={value}>      <MyComponent />    </Context.Provider>  );}

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

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

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

JavaScript import { useContext } from ‘react’; function MyComponent() { const value = useContext(Context); return <span>{value}</span>; }

12345import { useContext } from ‘react’;function MyComponent() {  const value = useContext(Context);  return <span>{value}</span>;}

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

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

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

JavaScript function MyComponent() { return ( <Context.Consumer> {value => <span>{value}</span>} </Context.Consumer> ); }

1234567function MyComponent() {  return (    <Context.Consumer>      {value => <span>{value}</span>}    </Context.Consumer>  );}

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

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

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

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

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

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

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

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

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

тему

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

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

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

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

набор услуг

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

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

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

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

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

JavaScript function Application() { const userName = “John Smith”; return <UserInfo userName={userName} />; } function UserInfo({ userName }) { return <span>{userName}</span>; }

1234567function Application() {  const userName = “John Smith”;  return <UserInfo userName={userName} />;}function UserInfo({ userName }) {  return <span>{userName}</span>;}

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

React JS. Основы

Изучите основы ReactJS на практическом примере по созданию учебного веб-приложения

Получить курс сейчас!

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

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

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

JavaScript function Application() { const userName = “John Smith”; return ( <Layout userName={userName}> Main content </Layout> ); } function Layout({ children, userName }) { return ( <div> <Header userName={userName} /> <main> {children} </main> </div> ) } function Header({ userName }) { return ( <header> <UserInfo userName={userName} /> </header> ); } function UserInfo({ userName }) { return <span>{userName}</span>; }

12345678910111213141516171819202122232425262728function Application() {  const userName = “John Smith”;  return (    <Layout userName={userName}>      Main content    </Layout>  );}function Layout({ children, userName }) {  return (    <div>      <Header userName={userName} />      <main>        {children}      </main>    </div>  )}function Header({ userName }) {  return (    <header>      <UserInfo userName={userName} />    </header>  );}function UserInfo({ userName }) {  return <span>{userName}</span>;}

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

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

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

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

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

JavaScript import { useContext, createContext } from ‘react’; const UserContext = createContext(‘Unknown’); function Application() { const userName = “John Smith”; return ( <UserContext.Provider value={userName}> <Layout> Main content </Layout> </UserContext.Provider> ); } function Layout({ children }) { return ( <div> <Header /> <main> {children} </main> </div> ); } function Header() { return ( <header> <UserInfo /> </header> ); } function UserInfo() { const userName = useContext(UserContext); return <span>{userName}</span>; }

123456789101112131415161718192021222324252627282930313233import { useContext, createContext } from ‘react’;const UserContext = createContext(‘Unknown’);function Application() {  const userName = “John Smith”;  return (    <UserContext.Provider value={userName}>      <Layout>        Main content      </Layout>    </UserContext.Provider>  );}function Layout({ children }) {  return (    <div>      <Header />      <main>        {children}      </main>    </div>  );}function Header() {  return (    <header>      <UserInfo />    </header>  );}function UserInfo() {  const userName = useContext(UserContext);  return <span>{userName}</span>;}

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

Разберемся подробнее, что было сделано. Сначала создается контекст 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 /> немедленно выполнит повторный рендеринг, чтобы отобразить последнее значение контекста:

JavaScript import { createContext, useEffect, useState } from ‘react’; const UserContext = createContext(‘Unknown’); function Application() { const [userName, setUserName] = useState(‘John Smith’); useEffect(() => { setTimeout(() => { setUserName(‘Smith, John Smith’); }, 2000); }, []); return ( <UserContext.Provider value={userName}> <Layout> Main content </Layout> </UserContext.Provider> ); } // …

123456789101112131415161718import { createContext, useEffect, useState } from ‘react’;const UserContext = createContext(‘Unknown’);function Application() {  const [userName, setUserName] = useState(‘John Smith’);  useEffect(() => {    setTimeout(() => {      setUserName(‘Smith, John Smith’);    }, 2000);  }, []);  return (    <UserContext.Provider value={userName}>      <Layout>        Main content      </Layout>    </UserContext.Provider>  );}// …

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

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

JavaScript function UserInfo() { const userName = useContext(UserContext); return <span>{userName}</span>; }

1234function UserInfo() {  const userName = useContext(UserContext);  return <span>{userName}</span>;}

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

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

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

JavaScript import { createContext, useState, useContext, useMemo } from ‘react’; const UserContext = createContext({ userName: ”, setUserName: () => {}, }); function Application() { const [userName, setUserName] = useState(‘John Smith’); const value = useMemo( () => ({ userName, setUserName }), [userName] ); return ( <UserContext.Provider value={value}> <UserNameInput /> </UserContext.Provider> ); } function UserNameInput() { const { userName, setUserName } = useContext(UserContext); const changeHandler = event => setUserName(event.target.value); return ( <input type=”text” value={userName} onChange={changeHandler} /> ); } function UserInfo() { const { userName } = useContext(UserContext); return <span>{userName}</span>; }

123456789101112131415161718192021222324252627282930313233import { createContext, useState, useContext, useMemo } from ‘react’;const UserContext = createContext({  userName: ”,  setUserName: () => {},});function Application() {  const [userName, setUserName] = useState(‘John Smith’);  const value = useMemo(    () => ({ userName, setUserName }),     [userName]  );    return (    <UserContext.Provider value={value}>      <UserNameInput />    </UserContext.Provider>  );}function UserNameInput() {  const { userName, setUserName } = useContext(UserContext);  const changeHandler = event => setUserName(event.target.value);  return (    <input      type=”text”      value={userName}      onChange={changeHandler}    />  );}function UserInfo() {  const { userName } = useContext(UserContext);  return <span>{userName}</span>;}

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

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

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

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

5. Вывод

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

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

Автор: Dmitri Pavlutin

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

Оставьте ответ