Создаем калькулятор-конвертер на базе 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-документ, содержащий в себе состояния компонентов. 

Файловый менеджер в VS Code

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

Конфигурация файла store.js

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

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

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

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

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

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

Содержимое файла historySlice

  • Импорт функции 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'

Импорт Redux-компонентов

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

 

Подключение провайдера Redux в React-приложении

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

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

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

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

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

Импорт хуков из Redux

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

Переменные управления состоянием в Redux

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

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

Переменная history из Redux

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

Интерфейс конвертера

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

Кнопка Add to history для изменения Redux-состояния

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

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

Redux-состояние в интерфейсе компонента App

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

Кнопка Add to history в интерфейсе калькулятора

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

Измененный код рендеринга истории операций

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

Состояние history в Redux

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

Комьюнити теперь в Телеграм

Подпишитесь и будьте в курсе последних IT-новостей

Подписаться

Что такое Router

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

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

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

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

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

Подключаем 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. 

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

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

      } />
    • Путь до конвертера:

      } />
    • Путь до калькулятора:

      } />

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

Список ссылкой на роуты в меню

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

    Calculator
  • Для конвертера – так: 

    Converter
    

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

Базовая ссылка

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

Роут до конвертера React

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

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

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

источник

Related Posts