Хук 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
не изменился.Обработка форм в React с помощью хуков
2 года назад·3 мин. на чтение
В этой статье рассмотрим, как обрабатывать формы в приложениях react с помощью хуков.
Формы
Формы позволяют нам принимать данные от пользователей и отправлять их на сервер для обработки. Это различные типы форм, такие как «Вход», «Регистрация» и т.д. В HTML5 есть элементы формы, такие какinput
, textarea
, select
, они поддерживают собственное внутреннее состояние в DOM, но в React мы поддерживаем состояние элементов формы внутри компонента, чтобы мы могли иметь полный контроль над элементами формы.
Что такое обработка форм
Обработка форм означает, как мы обрабатываем данные формы, когда пользователь изменяет значение или отправляет форму. Давайте посмотрим на пример того, как мы обрабатываем данные инпута с помощью хуков React.В приведенном выше коде мы установили для атрибутаimport React,{useState} from 'react'; function Form() { const [name,setName] = useState(''); function handleNameChange(e){ setName(e.target.value) } function handleSubmit(e) { e.preventDefault() // останавливаем перезагрузку страницы по умолчанию console.log(name); } return ( <form onSubmit={handleSubmit}> <input placeholder="Name" value={name} onChange={handleNameChange}/> <button>Submit</button> </form> ) }
value
элемента input
свойство name
, а метод обработчика событий onChange
- handleNameChange
- запускается каждый раз, когда мы вводим некоторые данные в элемент input
, и он обновляет свойство name
с помощью метода setName
, так что мы продолжаем синхронизировать значение с состоянием react.
handleSubmit
используется для отправки формы.
Элемент select
<select>
помогает нам создать выпадающий список. Давайте посмотрим на пример того, как создать выпадающий список и обрабатывать данные.
Здесь мы создали выпадающий список фреймворков в элементеimport React,{useState} from 'react'; function FormSelect() { // начальное значение состояния - строка 'react' const [framework, setFramework] = useState('react'); function handleChange(e){ setFramework(e.target.value); }; function handleSubmit(e){ e.preventDefault(); console.log(framework); }; return ( <form onSubmit={handleSubmit}> <h2>Choose your framework</h2> <select onChange={handleChange} value={framework}> <option value="react">React</option> <option value="angular">Angular</option> <option value="vue">Vue</option> </select> <button type="submit">Submit</button> </form> ); }
<select>
мы установили атрибут value
для свойства framework
и добавили обработчик событий onChange
.
Вложенные элементы <option>
содержат атрибут value
, который содержит данные, так что всякий раз, когда мы выбираем конкретный параметр, вызывается метод handleChange
и изменяет значение свойства framework
значением атрибута <option>
.
Вы видели, что элемент select
также следует аналогичному шаблону, как и элемент input
, так почему бы нам не создать пользовательский хук и повторно использовать его в каждом элементе формы?
Создание пользовательских хуков для обработки формы
Здесь мы создали пользовательский хук под названиемimport React, {useState} from 'react'; function useInput(initialValue) { const [value,setValue] = useState(initialValue); function handleChange(e) { setValue(e.target.value); } return [value,handleChange]; }
useInput
, давайте его применим.
Использование пользовательского хука useInput()
Теперь наш компонентfunction LoginForm(){ const [email, setEmail] = useInput(''); const [password, setPassword] = useInput(''); function handleSubmit(e){ e.preventDefault(); console.log(email,password) } return ( <form onSubmit={handleSubmit}> <input placeholder="Email" type="email" value={email} onChange={setEmail}/> <input placeholder="password" type="password" value={password} onChange={setPassword}/> <button>Submit</button> </form> ) }
LoginForm
выглядит намного чище с помощью пользовательского хука useInput()
. Точно так же мы можем использовать наш хук useInput
с другими элементами формы.
Пример с Radio button
function RadioButtons() { const [data] = useState({male:"male",female:"female",other:"other"}) const [gender, setGender] = useInput(""); function handleSubmit(e) { e.preventDefault(); console.log(gender); } return ( <form onSubmit={handleSubmit}> <h1>Select Your Gender</h1> <div> <input type="radio" id={data.male} value={data.male} checked={data.male === gender} onChange={setGender}/> <label htmlFor={data.male}>Male</label> </div> <div> <input type="radio" id={data.female} value={data.female} checked={data.female === gender} onChange={setGender}/> <label htmlFor={data.female}>Female</label> </div> <div> <input type="radio" id={data.other} value={data.other} checked={data.other === gender} onChange={setGender}/> <label htmlFor={data.other}>Other</label> </div> <button>submit</button> </form> ) }
Пример элемента Textarea
function Comments() { const [comment, setComment] = useInput(""); function handleSubmit(e) { e.preventDefault(); console.log(comment); } return( <form onSubmit={handleSubmit}> <textarea value={comment} onChange={setComment}/> <button>submit</button> </form> ) }