Иногда при создании приложения на React бывает сложно передавать реквизиты от одного компонента к другому без сверления реквизитов. Одним из встроенных инструментов React для решения этой проблемы является хук useContext.
Контекст React позволяет вам обернуть ваши компоненты в провайдера контекста. Значения, которые вам нужно передавать компонентам, могут быть инициализированы на верхнем уровне, а затем доступны в любом компоненте с помощью хука useContext.
Давайте рассмотрим простой пример.
Допустим, у нас есть приложение, которое содержит два компонента: один — это текстовый ввод, а другой — компонент, который будет отображать значение, введенное пользователем.
Вот структура файла.
К сожалению, мы не можем просто передать вводимое значение между братьями и сестрами Input и Result. В действительности, в этом очень простом примере лучшим способом решения проблемы было бы поднять состояние в компонент App, а затем передать его вниз в каждый из дочерних компонентов. Но допустим, вы создаете более сложное приложение, и вам нужно передать состояние на несколько уровней вниз по дереву компонентов, избегая при этом prop drilling — вот тут-то и приходит на помощь Context.
Стартовые файлы для этого примера можно найти здесь.
Сначала мы хотим создать новый файл и создать наш контекст с помощью React.createContext
.
import * as React from "react"
export type InputValues = {
nameValue: string
setNameValue: React.Dispatch<React.SetStateAction<string>>
}
export const InputContext = React.createContext<InputValues>({
nameValue: "",
setNameValue: () => console.info("Name not yet initialised"),
})
В объекте createContext вам нужно будет добавить и инициализировать все необходимые значения. Здесь мы установили nameValue
, которое будет использоваться в компоненте Result для отображения имени, и setNameValue
, которое будет использоваться для установки значения в компоненте Input.
Далее мы создадим собственный хук, который позже сможем использовать в провайдере.
import * as React from "react"
import { InputValues } from "./input-context"
export function useInputProvider(): InputValues {
const [nameValue, setNameValue] = React.useState("")
return {
nameValue,
setNameValue,
}
}
Здесь мы просто устанавливаем nameValue
и setNameValue
с помощью хука React useState и возвращаем их для использования в нашем следующем шаге.
Теперь нам нужно перейти в наш файл App и обернуть наши компоненты Input и Result в контекстный провайдер.
import { Input, Result } from "./components"
import { InputContext } from "./context"
import { useInputProvider } from "./context/use-input-provider"
function App() {
const nameValue = useInputProvider()
return (
<div className="inputForm">
<InputContext.Provider value={nameValue}>
<Input />
<Result />
</InputContext.Provider>
</div>
)
}
export default App
Итак, мы используем InputContext
, который мы создали в первом шаге, и обернем дочерние компоненты в провайдер. Затем мы можем использовать хук useInputProvider
, который мы установили во втором шаге, чтобы передать nameValue
и setNameValue
в качестве значения провайдера.
Теперь, когда мы настроили провайдера, как нам получить доступ к значениям в наших дочерних компонентах?
Сначала перейдем к нашему компоненту Input.
import * as React from "react"
import { InputContext } from "../context"
export function Input(): JSX.Element {
const { setNameValue } = React.useContext(InputContext)
function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
setNameValue(e.target.value)
}
return (
<form>
<label htmlFor="name">Name: </label>
<input type="text" id="name" name="name" onChange={handleChange} />
</form>
)
}
Здесь нам нужно получить доступ к setNameValue
, чтобы установить значение имени на то, которое пользователь введет в поле ввода. Для этого мы можем использовать хук useContext
и передать наш InputContext
. Затем вы можете извлечь setNameValue
вот так —
const { setNameValue } = React.useContext(InputContext)
Затем вы можете продолжить и использовать setNameValue
, чтобы принять входное значение.
Наконец, давайте перейдем к компоненту Result и получим доступ к nameValue
таким же образом, используя useContext
.
import * as React from "react"
import { InputContext } from "../context"
export function Result() {
const { nameValue } = React.useContext(InputContext)
return <div>{nameValue}</div>
}
Затем мы можем передать nameValue
в <div>
для отображения результата.
Вот и все! Вы можете найти готовый код здесь.