Создаем калькулятор-конвертер на базе React. Часть 9: Подключаем Redux и Router

Поговорим о важных и популярных сторонних библиотеках для React: Redux и Router. Зачем они нужны и как можно применить эти библиотеки на практике. Расскажу на примере калькулятора, который мы создавали в предыдущих частях цикла. 

Предыдыдущая часть: Калькулятор-конвертер на базе React. Часть 8: Конвертер валют и собственный парсер данных

Что такое Redux

Redux – это сторонняя библиотека для управления состояниями JavaScript-приложений. Вы уже знаете, как работают состояния компонентов в Реакте. Так вот Redux позволяет эти состояния перенести из кучи разных элементов в единую систему контроля, находящуюся обособленно от остальных файлов проекта, но при этом всегда в зоне досягаемости. То есть состояние множества компонентов может содержаться в едином хранилище. Его можно обновлять из любого Реакт-компонента и отображать в любом Реакт-компоненте. 

Redux позволяет упростить запутанные структуры передачи данных от родительских компонентов к наследникам и от наследников обратно к родителям. 

Трудно сказать, насколько целесообразно использование Redux в нашем приложении. Об эту тему было сломано уже немало копий, и я не буду пересказывать статьи на тему того, как важно применять те или иные технологии только в тех ситуациях, когда они действительно нужны. Я лишь продемонстрирую базовый пример работы Redux, а вы уже сами решите, нужен ли он вам здесь. Либо вы просто обучитесь работе с Redux и сможете применять его в других приложениях. 

Устанавливаем Redux

Сначала нужно загрузить саму утилиту как npm-пакет и вместе с ней подтянуть все зависимые пакеты, чтобы полностью настроить базовый вариант хранилища состояний. 

  • Открываем терминал в директории с нашим проектом. 

  • Вводим команду npm install react-redux

  • Затем вводим npm install @reduxjs/toolkit

По окончании работы обеих команд можно переходить к настройке непосредственно в приложении.

Базовая настройка Redux

Для начала надо определиться с тем, как вообще будет использоваться Redux и для чего он нам нужен. Так как количество сценариев применения Redux ограничивается лишь фантазией программистов, то конкретные причины применения Redux никто назвать не сможет. 

В нашем случае Redux будет хранить в себе историю любых вычислений. Будь-то подсчеты в калькуляторе или какие-то операции, выполненные конвертером. То есть наше поле History превратится в общедоступное пространство, в которое можно перенести информацию из любого режима работы приложения, чтобы сохранить полученные значения. 

С этой информацией в голове переходим к созданию своего первого Redux-хранилища. 

Сначала создаем файл store.js и сохраняем его в нашем проекте. Это и будет JavaScript-документ, содержащий в себе состояния компонентов. 

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

  • Импортируем конфигурационный файл configureStore. 

    import { configureStore } from '@reduxjs/toolkit'
  • Затем импортируем функцию изменения состояния с помощью Redux (мы создадим ее чуть позже). 

    import historyReducer from './historySlice'
  • После этого экспортируем уже измененный объект configureStore во внешние пространства (чтобы к нему можно было обратиться из сторонних компонентов приложения). Внутри него укажем функцию-reducer, отвечающую за изменение Redux-состояния. 

    export default configureStore({ reducer: { history: historyReducer, }, })

Закончив с хранилищем, переходим к созданию метода для изменения состояния. Он будет содержаться в файле historySlice.js, который мы создадим в корневой директории проекта.

А вот его содержимое:

  • Импорт функции createSlice из пакета @reduxjs/toolkit:

    import { createSlice } from '@reduxjs/toolkit'
  • Создание переменной, содержащей в себе вызов функции createSlice с необходимыми данными: 

    export const historySlice = createSlice( )
  • В качестве аргумента для createSlice выступит объект со всей необходимой информацией и методами для управления состояниями:

    • Имя – name: history

    • Состояние по умолчанию – initialState: { value: [], }

    • Методы управления состоянием (функция-reducer). В нашем случае это функция updateHistory, принимающая аргументы из сторонних компонентов при вызове и обрабатывающая их внутри Redux.

updateHistory: (state, action) => 
  state.value.push(action.payload)
}

Объявив каждый элемент и описав метод управления состоянием, остается экспортировать это все во внешнюю среду, чтобы потом импортировать в программу.

  • Экспортируем метод. 

    export const { historyUpdate } = historySlice.actions
  • Экспортируем состояние. 

    export const historyState = state = state.history.value
  • Экспортируем метод reducer целиком. 

    export default historySlice.reducer

Готово. Можно переходить ко второй части. 

Подключаем Redux к нашему приложению

У нас есть внешнее состояние, но на текущий момент оно никак не используется в нашем приложении. Мы будем изменять его из двух компонентов нашей программы, а отображаться оно будет в общей части интерфейса, видимой при использовании и калькулятора, и конвертера. 

Для работы с Redux-состояниями в React-приложении, надо импортировать хранилище store и компонент Provider в корневой файл программы (в нашем случае это index.js).

Получится:

import store from './components/store'

import { Provider } from 'react-redux'

Provider – это родительский компонент-обертка, в который нужно поместить весь код приложения целиком. Ранее мы создавали ChakraProvider. Так что надо и его поместить внутрь Provider от Redux. Также важно не забыть передать store в качестве аргумента для Provider. 

<Provider store={store}> </Provider>

Для начала добавим наше внешнее состояние в компонент Converter, чтобы опробовать его в полной мере и понять, работает ли оно вообще. 

Импортируем весь список нужных нам элементов.

  • Хуки из библиотеки Redux:

    import { useSelector, useDispatch } from 'react-redux'
  • Методы и состояние из нашего файла historySlice:

    import { updateHistory, historyState } from '../historySlice

Далее создаем для них переменные внутри компонента Converter. Переменная history должна использовать хук useSelector на состоянии history, чтобы хранить его в компоненте, а переменная dispatch позволяет быстро обратиться к хуку useDispatch(). 

Дальше дело техники. Чтобы отобразить в интерфейсе состояние history, мы просто записываем соответствующую переменную в методе return компонента Converter. А чтобы менять его, создадим отдельную клавишу, активирующую метод dispatch. 

В нашем случае нужно передавать в Redux запрос на запуск метода updateHistory с аргументом result (так как мы планируем передавать значение вычисления в конвертере). 

<Button onClick={() => dispatch(updateHistory(result))}

Получится подобный интерфейс. Он показывает результат преобразования одной единицы измерения в другую и кнопку отправки этого значения в состояние history.

При нажатии на эту кнопку результат конвертации попадет в Redux-состояние history и отобразится выше. 

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

Поэтому мы импортируем все те же данные из Redux и historySlice, но уже в компонент App. Там создадим переменную history, обращающуюся к хуку useSelector, и вернем ее в return.

Теперь можно добавить кнопку Add to history в любой компонент приложения, в том числе и в калькулятор. Работать она будет так же, как и любые другие аналогичные кнопки в приложении. 

Можно слегка модифицировать содержимое переменной history в компоненте App, чтобы элементы состояния отображались в отдельных кнопках.

Получится вот такой интерфейс. 

Также в нашем приложении Redux может упростить процесс реализации Drag & Drop.

Что такое Router

Router – это популярный инструмент для Реакт-программ и один из важнейших компонентов любого веб-приложения, позволяющий связать отдельные компоненты вашего проекта с конкретными адресами в браузере. 

Каждый компонент для браузера выглядит именно как отдельная страница, что позволяет оставлять ссылки на отдельные компоненты приложения. Например, в нашем случае вы можете создать ссылку сразу на калькулятор, а не на конвертер (или наоборот). Также это поможет сохранять состояние приложения при его перезагрузке. Сейчас при нажатии F5 мы автоматически переключаемся на калькулятор. 

Устанавливаем Router

Мы будем использовать React-Router. Есть и другие варианты роутинга, но этот показался мне наиболее простым. С ним легче всего разобраться, да и процесс установки укладывается в одну команду. 

  • Открываем терминал в директории с нашим проектом. 

  • Вводим команду npm install react-router-dom@6

По завершении работы команды переходим к настройки роутинга в приложении. 

Подключаем Router к нашему приложению

Подключается роутер довольно легко, так же, как и другие сторонние библиотеки. Нужно просто обернуть приложение в компонент, идущий в комплекте с React Router. 

Импортируем нужный компонент в index.js. 

import { BrowserRouter } from 'react-router-dom'

Теперь оборачиваем нашу программу в роутер. Нужно «положить» все, что идет уровнем ниже ChakraProvider, в BrowserRouter, как показано на скриншоте. 

После этого переходим к настройке роутинга в компоненте App.js. Так как это практически корень нашей программы, отсюда мы и будем перемещаться в разные части созданного нами инструмента. 

Импортируем сюда три компонента, отвечающих за базовую функциональность роутера. Это блок Routes, в котором будут содержаться все пути (ссылки на отдельные компоненты приложения), блок Route, хранящий в себе отдельный компонент для рендеринга, и Link – элемент, позволяющий переключаться между разными элементами страницы.

import { Routes, Route, Link } from 'react-router-dom'

Теперь пути нужно разместить в интерфейсе. Их можно спокойной прописать в методе return того компонента, который является корневым в вашем приложении. Все сразу они отображаться не будут, потому что роутинг работает как условный рендеринг, то есть отображаемый на странице контент будет зависеть от выбранного на текущий момент пути. 

  • Вместо переменной application, использованной ранее, создаем блок Router. 

    <Routes> </Routes>
  • Внутри блока указываем все нужные на пути, прописывая для них ссылку и компонент, который надо отобразить:

    • Путь до базового компонента (то есть того, что будет отображаться в интерфейсе программы по умолчанию). Так как у нас в программе нет главного экрана или его аналога, то мы по базовому пути будем выдавать калькулятор:

      <Route path="/" element={<Calculator />} />
    • Путь до конвертера:

      <Route path="converter" element={<Converter />} />
    • Путь до калькулятора:

      <Route path="calculator" element={<Calculator />} />

Осталось лишь сделать интерфейс для переключения между путями. У нас уже есть меню, отвечающее за работу условного рендеринга (то есть то, что позволяет выбирать значение переменной application внутри компонента App). Вот его и модифицируем, заменив кнопки, меняющие значение переменной application на элементы Link, а также URL в адресной строке браузера. 

  • Для калькулятора этот элемент будет выглядеть так: 

    <Link to="/calculator">Calculator</Link>
  • Для конвертера – так: 

    <Link to="/converter">Converter</Link

Вот как будет выглядеть приложение при запуске (обратите внимание на адресную строку).

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

Теперь при ручном вводе адреса /converter вы будете попадать в нужную часть программы, и это состояние приложения не будет сбрасываться при перезагрузке страницы. 

Вместо заключения

Мы изучили два важных элемента React-приложений, часто используемых в проектах различных масштабов. Даже если они и не нужны в таком мелком приложении, как калькулятор, то в других веб-сервисах они точно пригодятся.

Продолжение: Калькулятор-конвертер на базе React. Часть 10: Деплой

Межтекстовые Отзывы
Посмотреть все комментарии
guest