useContext¶
useContext
- это хук React, который позволяет вам читать и подписываться на контекст из вашего компонента.
1 |
|
Описание¶
useContext(SomeContext)
¶
Вызовите useContext
на верхнем уровне вашего компонента для чтения и подписки на контекст.
1 2 3 4 5 6 |
|
Параметры¶
SomeContext
: Контекст, который вы ранее создали с помощьюcreateContext
. Сам контекст не хранит информацию, он только представляет тип информации, которую вы можете предоставить или прочитать из компонентов.
Возвращает¶
useContext
возвращает значение контекста для вызывающего компонента. Оно определяется как value
, переданное ближайшему SomeContext.Provider
над вызывающим компонентом в дереве. Если такого провайдера нет, то возвращаемое значение будет defaultValue
, которое вы передали в createContext
для этого контекста. Возвращаемое значение всегда актуально. React автоматически перерисовывает компоненты, которые читают некоторый контекст, если он меняется.
Ограничения¶
- Вызов
useContext()
в компоненте не влияет на провайдеров, возвращаемых из этого же компонента. Соответствующий<Context.Provider>
должен находиться выше компонента, выполняющего вызовuseContext()
. - React автоматически перерисовывает все дочерние компоненты, использующие определенный контекст, начиная с провайдера, получившего другое
value
. Предыдущее и последующее значения сравниваются с помощью сравненияObject.is
. Пропуск повторных рендеров с помощьюmemo
не мешает дочерним компонентам получать свежие значения контекста. - Если ваша система сборки выдает дубликаты модулей на выходе (что может произойти при использовании симлинков), это может нарушить контекст. Передача чего-либо через контекст работает только в том случае, если
SomeContext
, который вы используете для предоставления контекста, иSomeContext
, который вы используете для его чтения, являются точно одним и тем же объектом, что определяется сравнением===
.
Использование¶
Передача данных глубоко в дерево¶
Вызовите useContext
на верхнем уровне вашего компонента для чтения и подписки на контекст.
1 2 3 4 5 6 |
|
useContext
возвращает значение контекста для контекста, который вы передали. Чтобы определить значение контекста, React просматривает дерево компонентов и находит ближайшего поставщика контекста выше для данного контекста.
Чтобы передать контекст Button
, оберните его или один из его родительских компонентов в соответствующий провайдер контекста:
1 2 3 4 5 6 7 8 9 10 11 |
|
Не имеет значения, сколько слоев компонентов находится между провайдером и Button
. Когда Button
в любом месте внутри Form
вызывает useContext(ThemeContext)
, он получит "dark"
в качестве значения.
Поиск ближайшего провайдера
useContext()
всегда ищет ближайшего провайдера выше компонента, который его вызывает. Она ищет вверх и не рассматривает провайдеров в компоненте, из которого вы вызываете useContext()
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
|
Обновление данных, переданных через контекст¶
Часто требуется, чтобы контекст менялся с течением времени. Чтобы обновить контекст, объедините его с состоянием. Объявите переменную состояния в родительском компоненте и передайте текущее состояние как значение контекста провайдеру.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
Теперь любой Button
внутри провайдера будет получать текущее значение theme
. Если вы вызовете setTheme
для обновления значения theme
, которое вы передаете провайдеру, все компоненты Button
будут перерисованы с новым значением 'light'
.
Примеры обновления контекста¶
1. Обновление значения через контекст¶
В этом примере компонент MyApp
хранит переменную состояния, которая затем передается провайдеру ThemeContext
. Установка флажка "Темный режим" обновляет состояние. Изменение предоставленного значения пересматривает все компоненты, использующие данный контекст.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
|
Обратите внимание, что value="dark"
передает строку "dark"
, а value={theme}
передает значение переменной JavaScript theme
с фигурными скобками JSX. Фигурные скобки также позволяют передавать контекстные значения, которые не являются строками.
2. Обновление объекта через контекст¶
В этом примере есть переменная состояния currentUser
, которая содержит объект. Вы объединяете { currentUser, setCurrentUser }
в один объект и передаете его вниз через контекст внутри value={}
. Это позволяет любому компоненту ниже, например, LoginButton
, читать и currentUser
, и setCurrentUser
, а затем вызывать setCurrentUser
, когда это необходимо.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
|
3. Множественные контексты¶
В этом примере есть два независимых контекста. ThemeContext
предоставляет текущую тему, которая является строкой, а CurrentUserContext
содержит объект, представляющий текущего пользователя.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
|
4. Извлечение провайдеров в компонент¶
По мере роста вашего приложения ожидается, что у вас будет "пирамида" контекстов ближе к корню вашего приложения. В этом нет ничего плохого. Однако, если вам эстетически не нравится вложенность, вы можете извлечь провайдеров в один компонент. В этом примере MyProviders
скрывает "сантехнику" и отображает переданные ему дочерние элементы внутри необходимых провайдеров. Обратите внимание, что состояния theme
и setTheme
нужны в самом MyApp
, поэтому MyApp
по-прежнему владеет этой частью состояния.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
|
5. Масштабирование с помощью контекста и редуктора¶
В больших приложениях часто используется сочетание контекста с редьюсером для извлечения логики, связанной с некоторым состоянием, из компонентов. В этом примере вся "проводка" спрятана в TasksContext.js
, который содержит редуктор и два отдельных контекста.
Прочитайте полное прохождение этого примера.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
|
Указание запасного значения по умолчанию¶
Если React не может найти поставщиков данного контекста в родительском дереве, значение контекста, возвращаемое useContext()
, будет равно значению по умолчанию, которое вы указали при создании контекста:
1 |
|
Значение по умолчанию никогда не изменяется. Если вы хотите обновить контекст, используйте его вместе с состоянием, как описано выше.
Часто вместо null
можно использовать какое-то более значимое значение по умолчанию, например:
1 |
|
Таким образом, если вы случайно отобразите какой-либо компонент без соответствующего провайдера, он не сломается. Это также поможет вашим компонентам хорошо работать в тестовой среде без установки большого количества провайдеров в тестах.
В примере ниже кнопка "Toggle theme" всегда светлая, потому что она находится вне любого провайдера контекста темы, а значение контекстной темы по умолчанию - 'light'
. Попробуйте изменить тему по умолчанию на 'dark'
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
|
Переопределение контекста для части дерева¶
Вы можете переопределить контекст для части дерева, обернув эту часть в провайдер с другим значением.
1 2 3 4 5 6 7 |
|
Вы можете вложить и переопределить провайдеров столько раз, сколько вам нужно.
Примеры переопределения контекста¶
1. Переопределение темы¶
Здесь кнопка внутри Footer
получает другое значение контекста ("light"
), чем кнопки снаружи ("dark"
).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
|
2. Автоматически вложенные заголовки¶
Вы можете "накапливать" информацию при вложении провайдеров контекста. В этом примере компонент Section
отслеживает LevelContext
, который определяет глубину вложенности раздела. Он считывает LevelContext
из родительской секции и предоставляет своим дочерним секциям номер LevelContext
, увеличенный на единицу. В результате компонент Heading
может автоматически решать, какой из тегов <h1>
, <h2>
, <h3>
, ..., использовать, основываясь на том, сколько компонентов Section
вложено в него.
Прочитайте подробное описание этого примера.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
1 2 3 |
|
Оптимизация рендеринга при передаче объектов и функций¶
Вы можете передавать любые значения через контекст, включая объекты и функции.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Здесь контекстное значение - это объект JavaScript с двумя свойствами, одно из которых - функция. Всякий раз, когда MyApp
перерисовывается (например, при обновлении маршрута), это будет разный объект, указывающий на разную функцию, поэтому React также должен будет перерисовать все компоненты в глубине дерева, которые вызывают useContext(AuthContext)
.
В небольших приложениях это не является проблемой. Однако нет необходимости перерисовывать их, если базовые данные, такие как currentUser
, не изменились. Чтобы помочь React воспользоваться этим фактом, вы можете обернуть функцию login
в useCallback
и обернуть создание объекта в useMemo
. Это оптимизация производительности:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
В результате этого изменения, даже если MyApp
потребуется перерендеринг, компонентам, вызывающим useContext(AuthContext)
, не потребуется перерендеринг, если только currentUser
не изменился.
Подробнее о useMemo
и useCallback
Устранение неполадок¶
Мой компонент не видит значение от моего провайдера¶
Есть несколько распространенных способов, как это может произойти:
- Вы отображаете
<SomeContext.Provider>
в том же компоненте (или ниже), где вы вызываетеuseContext()
. Переместите<SomeContext.Provider>
выше и вне компонента, вызывающегоuseContext()
. - Возможно, вы забыли обернуть ваш компонент
<SomeContext.Provider>
, или вы поместили его в другую часть дерева, чем думали. Проверьте правильность иерархии с помощью React DevTools. - Возможно, вы столкнулись с какой-то проблемой сборки с вашим инструментарием, из-за которой
SomeContext
, как видно из предоставляющего компонента, иSomeContext
, как видно из читающего компонента, являются двумя разными объектами. Это может произойти, например, если вы используете симлинки. Вы можете проверить это, присвоив им глобальные значенияwindow.SomeContext1
иwindow.SomeContext2
, а затем проверив в консоли, равно лиwindow.SomeContext1 === window.SomeContext2
. Если они не одинаковы, исправьте эту проблему на уровне инструмента сборки.
Я всегда получаю undefined
из моего контекста, хотя значение по умолчанию другое¶
У вас может быть провайдер без value
в дереве:
1 2 3 4 |
|
Если вы забыли указать value
, это все равно, что передать value={undefined}
.
Вы также могли по ошибке использовать другое имя пропса:
1 2 3 4 |
|
В обоих этих случаях вы должны увидеть предупреждение от React в консоли. Чтобы исправить их, вызовите свойство value
:
1 2 3 4 |
|
Обратите внимание, что значение по умолчанию из вашего вызова createContext(defaultValue)
используется только если выше вообще нет подходящего провайдера. Если где-то в родительском дереве есть компонент <SomeContext.Provider value={undefined}>
, компонент, вызывающий useContext(SomeContext)
получит undefined
в качестве значения контекста.
Источник — https://react.dev/reference/react/useContext