Разработка сайтов в Донецке, ДНР. Как заменить Redux на React Hooks и Context API

Самый популярный способ обработки общего состояния приложения в React — использование фреймворка, такого как Redux. Совсем недавно команда React представила несколько новых функций, включая React Hooks и Context API. Эти две функции эффективно устранили множество проблем, с которыми сталкивались разработчики крупных проектов React. Одной из самых больших проблем было «просверливание пропеллеров», характерное для вложенных компонентов. Решение заключалось в использовании библиотеки управления состоянием, такой как Redux. К сожалению, это произошло за счет написания шаблонного кода. Но теперь можно заменить Redux на React Hooks и Context API.

В этом руководстве вы узнаете о новом способе обработки состояния в ваших проектах React без написания лишнего кода или установки множества библиотек — как в случае с Redux. Хуки React позволяют вам использовать локальное состояние внутри функциональных компонентов, а Context API позволяет вам делиться состоянием с другими компонентами.

Предпосылки

Чтобы следовать этому руководству, вам необходимо ознакомиться со следующими темами:

Реагировать

Реагировать на крючки

Редукс

Техника, которую вы здесь изучите, основана на паттернах, представленных в Redux. Это означает, что вам нужно иметь твердое понимание reducersи actionsпрежде чем продолжить. В настоящее время я использую Visual Studio Code, который сейчас является самым популярным редактором кода (особенно для разработчиков JavaScript). Если вы работаете в Windows, я бы порекомендовал вам установить Git Bash. Используйте терминал Git Bash для выполнения всех команд, представленных в этом руководстве. Cmder также является хорошим терминалом, способным выполнять большинство команд Linux в Windows.

Вы можете получить доступ к полному проекту, используемому в этом руководстве, в этом репозитории GitHub.

О новой технике управления состоянием

В проектах React нам нужно иметь дело с двумя типами состояний:

местное государство

глобальное состояние

Локальные состояния можно использовать только в компонентах, где они были определены. Глобальные состояния могут быть общими для нескольких компонентов. Раньше для определения глобального состояния требовалась установка фреймворка управления состоянием, такого как Redux или MobX. С React v16.3.0 был выпущен Context API, который позволяет разработчикам реализовать глобальное состояние без установки дополнительных библиотек.

Начиная с React v16.8, хуки позволили реализовать ряд функций React в компоненте без написания класса. Хуки привнесли огромные преимущества в то, как разработчики React пишут код. Это включает в себя повторное использование кода и более простые способы обмена состоянием между компонентами. В этом уроке нас будут интересовать следующие хуки React:

использование состояния

использованиередьюсер

useStateрекомендуется для обработки простых значений, таких как числа или строки. Однако, когда дело доходит до обработки сложных структур данных, вам понадобится useReducerхук. Для useState, вам нужна только одна setValue () функция для перезаписи существующих значений состояния.

Для useReducer, вы будете обрабатывать объект состояния, который содержит несколько значений с разными типами данных в древовидной структуре. Вам нужно будет объявить функции, которые могут изменить одно или несколько из этих значений состояния. Для таких типов данных, как массивы, вам потребуется объявить несколько неизменяемых функций для обработки действий добавления, обновления и удаления. Вы увидите пример этого в следующем разделе этого руководства.

Как только вы объявите свое состояние, используя либо useStateили useReducer, вам нужно будет поднять его, чтобы оно стало глобальным состоянием, используя React Context. Это делается путем создания объекта контекста с использованием createContextфункции, предоставляемой библиотекой React. Объект контекста позволяет совместно использовать состояние между компонентами без использования свойств.

Вам также потребуется объявить поставщика контекста для вашего объекта контекста. Это позволяет странице или компоненту-контейнеру подписаться на ваш контекстный объект для внесения изменений. Любой дочерний компонент контейнера сможет получить доступ к объекту контекста с помощью useContextфункции.

Теперь давайте посмотрим на код в действии.

Настройка проекта

Мы будем использовать create-react-app для быстрого запуска нашего проекта:

$ npx create-react-appreact-hooks-context-app

Далее давайте установим Semantic UI React, CSS-фреймворк на основе React. Это не требование; Мне просто нравится создавать красивые пользовательские интерфейсы без написания собственного CSS:

yarn add semantic-ui-reactfomantic-ui-css

Откройте src/index.jsи вставьте следующий импорт:

import ‘fomantic-ui-css/semantic.min.css’;

Это все, что нам нужно сделать, чтобы наш проект начал использовать Semantic UI. В следующем разделе мы рассмотрим, как мы можем объявить состояние с помощью useStateхука и поднять его до глобального состояния.

Пример счетчика: useState

Для этого примера мы создадим простую демонстрацию счетчика, состоящую из компонента с двумя кнопками и компонента дисплея. Мы введем countсостояние, которое будет глобально совместно использоваться двумя компонентами. Компоненты будут дочерними элементами CounterView, которые будут действовать как контейнер. Компонент кнопки будет иметь кнопки, которые либо увеличивают, либо уменьшают значение countсостояния.

Давайте начнем с определения нашего countсостояния в файле контекста с именем context/counter-context.js. Создайте это внутри srcпапки и вставьте следующий код:

import React, { useState, createContext } from «react»;

// Create Context Object

export const CounterContext = createContext () ;

// Create a provider for components to consume and subscribe to changes

export const CounterContextProvider = props => {

const [count, setCount] = useState (0) ;

return (

{props.children}

 

) ;

};

Мы определили состояние с именем countи установили значение по умолчанию 0. Все компоненты, которые потребляют, CounterContext.Providerбудут иметь доступ к countсостоянию и setCountфункции. Определим компонент для отображения countсостояния в src/components/counter-display.js:

import React, { useContext } from «react»;

import { Statistic } from «semantic-ui-react»;

import { CounterContext } from «.../context/counter-context»;

export default function CounterDisplay () {

const [count] = useContext (CounterContext) ;

return (

{count}

Counter

 

) ;

}

Далее определим компонент, который будет содержать кнопки увеличения и уменьшения stateкомпонента. Создайте файл src/components/counter-buttons.jsи вставьте следующий код:

import React, { useContext } from «react»;

import { Button } from «semantic-ui-react»;

import { CounterContext } from «.../context/counter-context»;

export default function CounterButtons () {

const [count, setCount] = useContext (CounterContext) ;

const increment = () => {

setCount (count + 1) ;

};

const decrement = () => {

setCount (count — 1) ;

};

return (

 

 

 

 

 

 

 

 

 

) ;

}

Как бы то ни было, useContextфункция не будет работать, так как мы не указали Provider. Давайте сделаем это сейчас, создав контейнер в формате src/views/counter-view.js. Вставьте следующий код:

import React from «react»;

import { Segment } from «semantic-ui-react»;

import { CounterContextProvider } from «.../context/counter-context»;

import CounterDisplay from «.../components/counter-display»;

import CounterButtons from «.../components/counter-buttons»;

export default function CounterView () {

return (

Counter

 

 

) ;

}

Наконец, давайте заменим существующий код App.jsследующим:

import React from «react»;

import { Container } from «semantic-ui-react»;

import CounterView from «. /views/counter-view»;

export default function App () {

return (

React Hooks Context Demo

 

) ;

}

Теперь вы можете запустить create-react-appсервер с помощью yarn startкоманды. Браузер должен запуститься и отобразить ваш счетчик. Нажмите кнопки, чтобы убедиться, что функции incrementи decrementработают.

Вы также можете протестировать этот код на CodePen.

Давайте перейдем к следующему разделу, где мы создадим более продвинутый пример с использованием useReducerхука.

Пример контактов: useReducer

В этом примере мы создадим базовую страницу CRUD для управления контактами. Он будет состоять из пары презентационных компонентов и контейнера. Также будет объект контекста для управления состоянием контактов. Поскольку наше дерево состояний будет немного сложнее, чем в предыдущем примере, нам придется использовать useReducerхук.

Создайте объект контекста состояния src/context/contact-context.jsи вставьте этот код:

import React, { useReducer, createContext } from «react»;

export const ContactContext = createContext () ;

const initialState = {

contacts: [

{

id: «098»,

name: «Diana Prince»,

email: «diana@us.army.mil»

},

{

id: «099»,

name: «Bruce Wayne»,

email: «bruce@batmail.com»

},

{

id: «100»,

name: «Clark Kent»,

email: «clark@metropolitan.com»

}

],

loading: false,

error: null

};

const reducer = (state, action) => {

switch (action.type) {

case «ADD_CONTACT»:

return {

contacts: [...state.contacts, action.payload]

};

case «DEL_CONTACT»:

return {

contacts: state.contacts.filter (

contact => contact.id≠= action.payload

)

};

case «START»:

return {

loading: true

};

case «COMPLETE»:

return {

loading: false

};

default:

throw new Error () ;

}

};

export const ContactContextProvider = props => {

const [state, dispatch] = useReducer (reducer, initialState) ;

return (

{props.children}

 

) ;

};

Создайте родительский компонент src/views/contact-view.jsи вставьте этот код:

import React from «react»;

import { Segment, Header } from «semantic-ui-react»;

import ContactForm from «.../components/contact-form»;

import ContactTable from «.../components/contact-table»;

import { ContactContextProvider } from «.../context/contact-context»;

export default function Contacts () {

return (

 

Contacts

 

 

 

) ;

}

Создайте компонент презентации src/components/contact-table.jsи вставьте этот код:

import React, { useState, useContext } from «react»;

import { Segment, Table, Button, Icon } from «semantic-ui-react»;

import { ContactContext } from «.../context/contact-context»;

export default function ContactTable () {

// Subscribe to `contacts` state and access dispatch function

const [state, dispatch] = useContext (ContactContext) ;

// Declare a local state to be used internally by this component

const [selectedId, setSelectedId] = useState () ;

const delContact = id => {

dispatch ({

type: «DEL_CONTACT»,

payload: id

}) ;

};

const onRemoveUser = () => {

delContact (selectedId) ;

setSelectedId (null) ; // Clear selection

};

const rows = state.contacts.map (contact => (

<Table.Row

key={contact.id}

onClick={ () => setSelectedId (contact.id) }

active={contact.id === selectedId}

>

{contact.id}

{contact.name}

{contact.email}

 

));

return (

 

 

Id

Name

Email

 

 

{rows}

<Button

floated="right"

icon

labelPosition="left"

color="red"

size="small"

disabled={! selectedId}

onClick={onRemoveUser}

>

Remove User

 

 

 

 

 

 

) ;

}

Создайте компонент презентации src/components/contact-form.jsи вставьте этот код:

import React, { useState, useContext } from «react»;

import { Segment, Form, Input, Button } from «semantic-ui-react»;

import _ from «lodash»;

import { ContactContext } from «.../context/contact-context»;

export default function ContactForm () {

const name = useFormInput («») ;

const email = useFormInput («») ;

// eslint-disable-next-lineno-unused-vars

const [state, dispatch] = useContext (ContactContext) ;

const onSubmit = () => {

dispatch ({

type: «ADD_CONTACT»,

payload: { id: _.uniqueId (10), name: name.value, email: email.value }

}) ;

// Reset Form

name.onReset () ;

email.onReset () ;

};

return (

 

 

 

 

 

 

 

 

 

 

 

) ;

}

function useFormInput (initialValue) {

const [value, setValue] = useState (initialValue) ;

const handleChange = e => {

setValue (e.target.value) ;

};

const handleReset = () => {

setValue («») ;

};

return {

value,

onChange: handleChange,

onReset: handleReset

};

}

Вставьте следующий код App.jsсоответственно:

import React from «react»;

import { Container } from «semantic-ui-react»;

import ContactView from «. /views/contact-view»;

export default function App () {

return (

React Hooks Context Demo

 

) ;

}

После внедрения кода страница вашего браузера должна обновиться. Чтобы удалить контакт, вам нужно сначала выбрать строку, а затем нажать кнопку «Удалить «. Чтобы создать новый контакт, просто заполните форму и нажмите кнопку «Новый контакт».

Вы также можете протестировать этот код на CodePen.

Просмотрите код, чтобы убедиться, что вы все поняли. Прочитайте комментарии, которые я включил в код.

Резюме

Я надеюсь, что эти примеры помогут вам понять, как можно управлять общим состоянием приложения в приложении React без Redux. Если бы вы переписали эти примеры без хуков и контекстного API, это привело бы к гораздо большему количеству кода. Видите, насколько проще писать код, не имея дело с пропсами?

Вы могли заметить, что во втором примере есть пара неиспользуемых переменных состояния — loadingи error. В качестве испытания вы можете продвинуть это приложение дальше, чтобы использовать их. Например, вы можете реализовать фиктивную задержку и заставить компоненты презентации отображать статус загрузки. Вы также можете пойти дальше и получить доступ к реальному удаленному API. Здесь errorпеременная состояния может быть полезна для отображения сообщений об ошибках.

Единственный вопрос, который вы можете задать себе сейчас: нужен ли Redux для будущих проектов? Один недостаток, который я заметил в этой технике, заключается в том, что вы не можете использовать расширение Redux DevTool для отладки состояния вашего приложения. Однако это может измениться в будущем с разработкой нового инструмента. Очевидно, что как разработчику вам все равно придется изучать Redux, чтобы поддерживать устаревшие проекты. Но если вы начинаете новый проект, вам нужно спросить себя и свою команду, действительно ли в вашем случае необходимо использование сторонней библиотеки управления состоянием.

Делитесь нашими материалами с друзьями!

 

 

Заказать разработку сайта