Хук useContext - как использовать контекст в React?
2 года назад·4 мин. на чтение
useContext - это React хук, который позволяет вам читать и подписываться на контекст из вашего компонента.
API хука useContext
const value = useContext(SomeContext)
useContext(SomeContext)
ВызовитеuseContext
на верхнем уровне вашего компонента для чтения и подписки на контекст.
import { useContext } from 'react'; function MyComponent() { const theme = useContext(ThemeContext); // ...
Параметры
SomeContext
: Контекст, который вы ранее создали с помощьюcreateContext
. Сам контекст не содержит информации, он только представляет тип информации, которую вы можете предоставить или прочитать из компонентов.
Что возвращает useContext
?
useContext
возвращает значение контекста для вызывающего компонента. Оно определяется как значение, переданное ближайшему SomeContext.Provider
, расположенному выше вызывающего компонента в дереве. Если такого провайдера нет, то возвращаемое значение будет значением по умолчанию (defaultValue
), которое вы передали в createContext
для данного контекста. React автоматически перерисовывает компоненты, которые читают некоторый контекст, если он изменяется.
Использование контекста
Передача данных вглубь дерева
ВызовитеuseContext
на верхнем уровне вашего компонента для чтения и подписки на контекст.
import { useContext } from 'react'; function Button() { const theme = useContext(ThemeContext); // ...
useContext
возвращает значение контекста для переданного вами контекста. Чтобы определить значение контекста, React просматривает дерево компонентов и находит ближайший вышеуказанный провайдер контекста для данного контекста.
Чтобы передать контекст кнопке, оберните ее или один из ее родительских компонентов в соответствующий провайдер контекста:
Не имеет значения, сколько слоев компонентов находится между провайдером и кнопкой. Когда кнопка в любом месте формы вызываетfunction MyPage() { return ( <ThemeContext.Provider value="dark"> <Form /> </ThemeContext.Provider> ); } function Form() { // ... отрисовывает кнопки внутри себя ... }
useContext(ThemeContext)
, она получит значение "dark"
.
import { createContext, useContext } from 'react'; const ThemeContext = createContext(null); export default function MyApp() { return ( <ThemeContext.Provider value="dark"> <Form /> </ThemeContext.Provider> ) } function Form() { return ( <Panel title="Welcome"> <Button>Sign up</Button> <Button>Log in</Button> </Panel> ); } function Panel({ title, children }) { const theme = useContext(ThemeContext); const className = 'panel-' + theme; return ( <section className={className}> <h1>{title}</h1> {children} </section> ) } function Button({ children }) { const theme = useContext(ThemeContext); const className = 'button-' + theme; return ( <button className={className}> {children} </button> ); }
Обновление данных, переданных через контекст
Часто бывает необходимо, чтобы контекст менялся с течением времени. Чтобы обновить контекст, вам нужно объединить его с состоянием. Объявите переменную state в родительском компоненте и передайте текущее состояние в качестве значения контекста провайдеру.Теперь любая кнопка внутри провайдера будет получать текущее значение темы. Если вы вызоветеunction MyPage() { const [theme, setTheme] = useState('dark'); return ( <ThemeContext.Provider value={theme}> <Form /> <Button onClick={() => { setTheme('light'); }}> Switch to light theme </Button> </ThemeContext.Provider> ); }
setTheme
для обновления значения темы, которое вы передаете провайдеру, все компоненты Button
будут заново отображаться с новым значением "light"
.
Указание значения по умолчанию
Если React не может найти ни одного провайдера данного контекста в родительском дереве, значение контекста, возвращаемое функциейuseContext()
, будет равно значению по умолчанию, которое вы указали при создании контекста:
Значение по умолчанию никогда не изменяется. Если вы хотите обновить контекст, используйте его вместе с состоянием, как описано выше. Часто вместоconst ThemeContext = createContext(null);
null
можно использовать какое-то более значимое значение по умолчанию, например:
Таким образом, если вы случайно отобразите какой-то компонент без соответствующего провайдера, он не сломается. Это также поможет вашим компонентам хорошо работать в тестовой среде без установки большого количества провайдеров в тестах. В приведенном ниже примере кнопка "Toggle theme" всегда светлая, потому что она находится вне любого провайдера контекста темы, а значение контекстной темы по умолчанию -const ThemeContext = createContext('light');
'light'
.
import { createContext, useContext, useState } from 'react'; const ThemeContext = createContext('light'); export default function MyApp() { const [theme, setTheme] = useState('light'); return ( <> <ThemeContext.Provider value={theme}> <Form /> </ThemeContext.Provider> <Button onClick={() => { setTheme(theme === 'dark' ? 'light' : 'dark'); }}> Toggle theme </Button> </> ) } function Form({ children }) { return ( <Panel title="Welcome"> <Button>Sign up</Button> <Button>Log in</Button> </Panel> ); } function Panel({ title, children }) { const theme = useContext(ThemeContext); const className = 'panel-' + theme; return ( <section className={className}> <h1>{title}</h1> {children} </section> ) } function Button({ children, onClick }) { const theme = useContext(ThemeContext); const className = 'button-' + theme; return ( <button className={className} onClick={onClick}> {children} </button> ); }
Переопределение контекста для части дерева
Вы можете переопределить контекст для части дерева, обернув эту часть в провайдер с другим значением.Вы можете вложить и переопределить провайдеров столько раз, сколько вам нужно.<ThemeContext.Provider value="dark"> ... <ThemeContext.Provider value="light"> <Footer /> </ThemeContext.Provider> ... </ThemeContext.Provider>
Оптимизация повторных рендерингов при передаче объектов и функций
Вы можете передавать любые значения через контекст, включая объекты и функции.Здесь значение контекста - это JavaScript объект с двумя свойствами, одно из которых - функция. Всякий раз, когдаfunction MyApp() { const [currentUser, setCurrentUser] = useState(null); function login(response) { storeCredentials(response.credentials); setCurrentUser(response.user); } return ( <AuthContext.Provider value={{ currentUser, login }}> <Page /> </AuthContext.Provider> ); }
MyApp
ререндерится (например, при обновлении маршрута), это будет другой объект, указывающий на другую функцию, поэтому React также придется перерендерить все компоненты в глубине дерева, которые вызывают useContext(AuthContext)
.
В небольших приложениях это не является проблемой. Однако нет необходимости перерисовывать их, если базовые данные, такие как currentUser
, не изменились. Чтобы помочь React воспользоваться этим фактом, вы можете обернуть функцию входа в систему в useCallback
и обернуть создание объекта в useMemo
.
В результате этого изменения, даже еслиimport { useCallback, useMemo } from 'react'; function MyApp() { const [currentUser, setCurrentUser] = useState(null); const login = useCallback((response) => { storeCredentials(response.credentials); setCurrentUser(response.user); }, []); const contextValue = useMemo(() => ({ currentUser, login }), [currentUser, login]); return ( <AuthContext.Provider value={contextValue}> <Page /> </AuthContext.Provider> ); }
MyApp
потребуется повторный рендеринг, компонентам, вызывающим useContext(AuthContext)
, не потребуется повторный рендеринг, если только currentUser
не изменился.Хуки useTransition и useDeferredValue в ReactJS 18
2 года назад·3 мин. на чтение
В React 18, релиз которого произошел в марте 2022, появилось много новых инструментов для написания производительных и отзывчивых приложений. Одним из заметных изменений является механизм рендеринга с новой ключевой концепцией: конкурентный рендеринг (concurrent rendering).
В этой статье повнимательнее рассмотрим два новых хука:
Какое обновление можно считать срочным, а какое обычным?
Хук
До React 18 все обновления состояния помечались как "срочные". Это означает, что все обновления состояния обрабатывались одинаково с одинаковым приоритетом.
С помощью
Когда использовать
Одним из примеров может быть список товаров с параметрами фильтрации.
Когда вы переключаете чекбоксы, чтобы выбрать размер или цвет одежды, вы ожидаете, что чекбоксы сразу же отобразят отмеченное или снятое состояние.
А сам список товаров, которые необходимо обновить согласно фильтрам, может быть отдельным и менее срочным обновлением.
Как использовать
Хук
Когда использовать
С помощью
Как использовать
useTransition()
и useDeferredValue()
.
Эти два хука дают возможность определять приоритет обновления состояния, или, скорее, указывать, является ли обновление менее важным, чем другие, и откладывать его в пользу более срочных.
- Срочные обновления: отражают прямое взаимодействие, такое как набор текста, клики, нажатия и т. д., т.е. то с чем взаимодействует пользователь. Когда вы вводите текст в поле ввода, вы хотите сразу увидеть введенный вами текст. В противном случае UI будет казаться медленным и подлагивать. Поэтому мы хотим сделать такие обновления приоритетным.
- Обычные обновления: переход пользовательского интерфейса из одного вида в другой. Пользователи знают, что представление должно измениться или обновиться (например, когда ожидается ответ на запрос данных). Даже если есть небольшая задержка, это можно рассматривать как ожидаемое поведение, и это не будет восприниматься как медлительность приложения.
Хук useTransition()
и функция startTransition()
До React 18 все обновления состояния помечались как "срочные". Это означает, что все обновления состояния обрабатывались одинаково с одинаковым приоритетом.
С помощью useTransition()
теперь можно пометить некоторые обновления состояния как несрочные.
Когда использовать useTransition()
?
Одним из примеров может быть список товаров с параметрами фильтрации.
Когда вы переключаете чекбоксы, чтобы выбрать размер или цвет одежды, вы ожидаете, что чекбоксы сразу же отобразят отмеченное или снятое состояние.
А сам список товаров, которые необходимо обновить согласно фильтрам, может быть отдельным и менее срочным обновлением.
Как использовать useTransition()
?
function App() { const [isPending, startTransition] = useTransition(); const [searchQuery, setSearchQuery] = useState(''); // запрос данных, который занимает некоторое время const filteredResults = getProducts(searchQuery); function handleQueryChange(event) { startTransition(() => { // оборачивая setSearchQuery() в startTransition(), // мы помечаем эти обновления как менее важные setSearchQuery(event.target.value); }); } return ( <div> <input type="text" onChange={handleQueryChange} /> {isPending && <span>Loading...</span>} <ProductsList results={filteredResults} /> </div> ); }
Хук useDeferredValue()
useDeferredValue()
очень похож на useTransition()
в том, что он позволяет отложить несрочное обновление состояния, но применяется его к части дерева.
Это похоже методы debounce и throttle, которые мы часто используем для отложенных обновлений. React будет работать с такими обновлениями, как только завершатся срочные обновления.
Когда использовать useDeferredValue()
?
С помощью useTransition()
вы сами решаете, когда конкретное обновление состояния может быть помечено как менее срочное. Но иногда такой возможности может и не быть, например, если фрагмент кода находится в сторонней библиотеке.
В таких случаях можно воспользоваться хуком useDeferredValue()
. С помощью useDeferredValue()
вы можете обернуть значение и пометить его изменения как менее важные и, следовательно, отложить повторный рендеринг.
useDeferredValue()
будет возвращать предыдущее значение до тех пор, пока есть более срочные обновления для завершения и отображения дерева с обновленным значением.
Как использовать useDeferredValue()
?
function ProductsList({ results }) { // deferredResults получат обновленные данные // когда завершатся срочные обновления const deferredResults = useDeferredValue(results); return ( <ul> {deferredResults.map((product) => ( <li key={product.id}>{product.title}</li> ))} </ul> ); }