- React — Управление состоянием с помощью контекстного API с хуками useState или useReducer
- Управление состоянием
- Что такое бурение реквизитов?
- Контекстный API
- Когда использовать Context
- Context Api с useState
- Контекстный API с useReducer
- useReducer
- useReducer с контекстным API
- useState VS useReducer в контекстном API
- Когда не стоит использовать Context API
React — Управление состоянием с помощью контекстного API с хуками useState или useReducer
Управление состоянием
В любом приложении react есть различные части пользовательского интерфейса, которые разделены на различные компоненты. Некоторым компонентам может потребоваться использовать состояние, объявленное или обновленное в другом компоненте. Традиционный способ сделать это — создать состояние в родительском компоненте и передать состояние в качестве props в дочерний компонент. Это работает, но для приложений с несколькими уровнями вложенных дочерних компонентов потребуется передавать реквизиты каждому вложенному дочернему компоненту. Этот процесс называется props drilling.
Что такое бурение реквизитов?
Prop drilling означает передачу данных от родителя всем вложенным дочерним элементам в дереве React.
Это работает до тех пор, пока нам не понадобится изменить код.
- Обновление значения prop для дочерних компонентов, например,
<Child1 state="data" />
и<Child2 state="data" />
, когда нам нужно обновить этот компонент, например, изменить состояние со строкового значения на значение массива, какstate=["data1", "data2"]
, потребует обновления всех props дочерних компонентов. - Предположим, что в процессе работы приложения наступит время переименовать реквизит, поскольку вы удаляете некоторые данные или передаете больше. Его необходимо изменить во всех местах иерархии.
Чтобы решить эту проблему, необходимо иметь подходящий инструмент для обработки состояния всего приложения. Существует множество вариантов, таких как redux, MobX, Flux и контекстный API. В этой статье вы узнаете, как использовать context api с usestate и usereducer, простое в использовании и легкое решение.
Контекстный API
«Context предоставляет способ передачи данных через дерево компонентов без необходимости передавать реквизиты вручную на каждом уровне». — React
Context api — это встроенный хук react.
Когда использовать Context
Context предназначен для обмена данными, которые можно считать глобальными для дерева компонентов React, такими как текущий аутентифицированный пользователь, тема или предпочитаемый язык.
Context Api использует два основных хука (createContext и useContext), а также один хук для установки и обновления состояния {этот хук не является обязательным, но он важен для обновления состояния}.
Давайте используем оба хука useState и useReducer только для того, чтобы получить общее представление о том, на что способен контекст. Во всех примерах мы будем использовать компонент переключения режимов свет/темнота.
Context Api с useState
Создайте jsx-файл context.jsx и начните его редактировать,
-
Создайте контекст и дайте ему имя, имя контекста может быть любым, для этого примера мы будем использовать StateContext.
-
Создайте провайдер состояний, передав ему дочерние элементы в качестве реквизитов (дочерними элементами может быть все, что вы передадите, и в нашем случае это все приложение, это также означает, что это компонент react, и мы обернем его в наше приложение).
-
Наконец, объявляем метод для использования нашего контекста
import React, { createContext, useContext, useState } from "react"
// create toggle context
const ToggleContext = createContext()
// create context provider
export const ToggleProvider = ({ children }) => {
const [data, setData] = useState({
darkMode: false,
})
// the value passed in here will be accessible anywhere in our application
// you can pass any value, in our case we pass our state and it's update method
return (
<ToggleContext.Provider value={{data, setData}}>
{children}
</ToggleContext.Provider>
)
}
// useToggleContext will be used to use and update state accross the app
// we can access to data and setData using this method
// anywhere in any component that's inside ToggleProvider
export const useToggleContext = useContext(ToggleContext)
- Давайте используем его сейчас, В вашем корневом файле т.е. App.jsx импортируйте StateProvider и оберните ваше приложение в него.
Обертывание наших компонентов внутри провайдера даст всем дочерним компонентам доступ к состоянию, объявленному в этом провайдере. Любой компонент вне обертки не будет иметь доступа к глобальному состоянию.
// import provider from context jsx
import { ToggleProvider } from "./context"
import Home from "./Home"
function App() {
// Wrap the all components inside provider
return (
<ToggleProvider>
{/* every other component */}
<Home />
</ToggleProvider>
)
}
export default App
- Теперь, когда состояние стало глобальным, давайте его используем. Создайте любые файлы, допустим Nav.jsx и Home.jsx. В этих файлах импортируйте useStateContext из context.jsx, давайте посмотрим его в действии.
// Nav.jsx
import { useToggleContext } from "./context"
const Nav = () => {
// declare state just like you did in context jsx
// But instead of useState, use useToggleContext
const [data, setData] = useToggleContext()
return (
<div>
<button
onClick={() => setData({
... data,
darkMode: !data.darkMode
})}
>
{data.darkMode ? "Change to Light Mode" : "Change To Dark Mode"}
</button>
</div>
)
}
// Home.jsx
import { useToggleContext } from "./context"
import Nav from "./Nav"
const Home = () => {
// declare state just like you did in context jsx
// But instead of useState, use useToggleContext
const [data, setData] = useToggleContext()
return (
<div
style={{
// update mode between dark and light
backgroundColor: data.darkMode ? "#000000" : "#ffffff",
color: data.darkMode ? "#ffffff" : "#000000"
}}
>
<Nav />
</div>
)
}
- При нажатии на кнопку переключения будет изменяться состояние, а также веб-стили.
Теперь у нас есть состояние. Вы можете использовать это состояние в любом компоненте, data можно использовать в качестве значения, а setData — для обновления данных.
Контекстный API с useReducer
useReducer
useReducer — это один из хуков, который помогает в управлении состояниями. Вы можете использовать этот хук как замену useState, он не обязательно требует context api, это отдельный хук.
Как он работает?
Для работы react reducer нам нужно начальное состояние, функция reducer и диспетчер для обновления данных.
Прежде чем погрузиться в контекстную сторону useReducer, давайте рассмотрим его использование в качестве отдельного хука.
import React, { useContext, createContext, useReducer } from "react"
// context for using state
const ToggleStateContext = createContext()
// context for updating state
const ToggleDispatchContext = createContext()
// reducer function
const reducer = (state, action) => {
const { type, payload } = action
case: "CHANGE_MODE":
return {
...state,
darkMode: payload
}
default:
return state
}
}
export const ToggleProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, {
darkMode: false
})
return (
<ToggleDispatchContext.Provider value={dispatch}>
<ToggleStateContext.Provider value={state}>
{children}
</ToggleStateContext.Provider>
</ToggleDispatchContext.Provider>
)
}
// use them context we've created
export const useToggleStateContext = () => useContext(ToggleStateContext)
export const useToggleDispatchContext = () => useContext(ToggleDispatchContext)
useReducer с контекстным API
Теперь, когда нам удалось использовать хук useReducer в компоненте, давайте воспользуемся им для контекстного API, не так ли? Как я уже говорил, для контекстного api требуется хук для установки и обновления состояния, поэтому, как мы реализовали useState, так же мы собираемся реализовать useReducer, давайте займемся этим.
Для этого мы создадим два контекста, один для диспетчеризации, другой для состояния, чтобы передавать значения состояния и диспетчеризации по-разному.
- Создайте провайдера контекста в файле context.jsx
Здесь функция reducer аналогична той, которую мы использовали в Nav.jsx.
// import provider from context jsx
import { ToggleProvider } from "./context"
import Home from "./Home"
function App() {
// Wrap the all components inside provider
return (
<ToggleProvider>
{/* every other component */}
<Home />
</ToggleProvider>
)
}
export default App
- Итак, у нас есть наш context api с useReducer, переходим к следующему шагу — обертыванию нашего приложения в Context provider
// import provider from context jsx
import { ToggleProvider } from "./context"
import Home from "./Home"
function App() {
// Wrap the all components inside provider
return (
<ToggleProvider>
{/* every other component */}
<Home />
</ToggleProvider>
)
}
export default App
- Теперь у нас есть глобально доступное состояние, давайте пойдем и используем или обновим его где-нибудь, скажем, на странице Nav, как мы делали в примере useState.
// Nav.jsx
import React from "react"
import { useToggleDispatchContext, useToggleStateContext } from "./context"
const Nav = () => {
const { darkMode } = useToggleStateContext()
const dispatch = useToggleDispatchContext()
return (
<div>
{/* this will update the specific state by checking the type */}
<button onclick={() => dispatch({
type: "CHANGE_MODE",
payload: !darkMode
})}>
{darkMode ? "Change To Light Mode" : "Change to Dark Mode"}
</button>
</div>
)
}
И в файле Home
// Home.jsx
import { useToggleStateContext } from "./context"
import Nav from "./Nav"
const Home = () => {
const { darkMode } = useToggleStateContext()
return (
<div
style={{
// update mode between dark and light
backgroundColor: data.darkMode ? "#000000" : "#ffffff",
color: data.darkMode ? "#ffffff" : "#000000"
}}
>
<Nav />
</div>
)
}
И все должно работать как ожидалось, но теперь с глобальным состоянием, которое вы можете использовать где угодно
useState VS useReducer в контекстном API
Хотя useState выглядит чище и проще в реализации, для больших приложений с большим количеством изменений состояния, useReducer даст вам больше контроля над состоянием.
Когда не стоит использовать Context API
Когда приложение требует большого количества обновлений состояния, при изменении состояния все дочерние приложения, использующие один и тот же провайдер, будут перерисовываться независимо от того, используют они обновленное состояние или нет.
Redux и другие сторонние библиотеки управления состоянием решают эту проблему. Это вопрос решения, действительно ли вам нужно использовать дополнительную библиотеку или нет, в зависимости от того, насколько велико ваше приложение и насколько часто будет обновляться состояние, требующее глобального управления состоянием.
Если вам понравилась эта статья, в наших блогах вы найдете еще больше подобных материалов, следите за нами на dev.to/clickpesa, medium.com/clickpesa-engineering-blog и clickpesa.hashnode.dev.
Счастливого хакинга!!!