Почему Редукс?

По мере роста проектов React растет и зависимость компонентов от состояния приложения. В конце концов передача состояния и свойств между компонентами может превратиться в сложную паутину, которую будет трудно понять. Это не только создает больше возможностей для неправильного поведения компонентов, но и затрудняет понимание разработчиками того, как компоненты обрабатывают и обмениваются данными друг с другом.

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

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

Наши данные теперь все в одном месте. Наше состояние — это просто объект JavaScript. Ничего особенного, но шаблон потока данных, который предоставляет Redux, прокладывает путь к более чистому и понятному коду.

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

Основы Редукса

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

В основе Redux лежат три основных аспекта: действия, редукторы и хранилище. Поток данных между этими тремя аспектами может быть упрощен в три этапа:

  1. Называется действие, которое затем
  2. Отправлено в функцию, которая затем
  3. Обновляет наше состояние в соответствии с этим действием

Action -> Function -> Updated State

Действие — это простой объект JavaScript с атрибутом типа. Этот атрибут типа описывает, что вы хотели бы сделать, например, UPDATE_COMMENT или FETCH_DATA.

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

Теперь, когда мы ввели редьюсеры, мы можем дать имя нашей «функции» в наших данных потоком выше и переписать его как:

Action -> Reducer -> Updated State

Независимо от того, насколько сложной становится реализация Redux, она всегда следует этому шаблону потока.

Но теперь, когда мы обновили наше состояние, как мы можем получить доступ к этим данным?
Вот тут-то и появляется store. Store — это наш дом для состояния всего нашего приложения. Если это звучит пугающе, помните, что состояние — это просто объект JavaScript с парами ключ/значение, как и любой другой объект. На самом деле это упрощает получение кусочков состояния, которые нужны конкретному компоненту в любой момент времени.

Примечание о хуках React

В выпуске версии 16.8 React представил хуки, простой способ использования состояния и других функций React без необходимости создания компонента класса.
React-Redux, библиотека привязки пользовательского интерфейса, которая соединяет Redux с React, добавила два хука. самостоятельно, чтобы предоставить нашим компонентам легкий доступ к нашему хранилищу Redux: useSelector и useDispatch.

Начиная

Сначала нам нужно создать новый магазин в файле store.js, как мы делали выше. Мы импортируем createStore из библиотеки Redux и передаем его нашим редукторам. Этот экземпляр хранилища будет тем, что мы передаем нашему приложению.

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

Загадка асинхронности

В этом примере мы собираемся сделать запрос на получение для приложения React для криптовалюты, которое извлекает и отображает рыночные данные для 100 самых популярных криптовалют. Мы будем использовать бесплатный API криптографических данных на CoinGecko.com, чтобы делать наши запросы.

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

Мы начнем с создания нашего действия в нашем операторе переключения редукторов “SET_COINS”.

(Примечание: соглашение для случаев в нашем операторе switch записывается как «DO_ACTION». Мне нравится устанавливать все это в константы и сохранять их в их собственном файле constants.js. Пример, который мы используем, будет записан как export const SET_COINS = "SET_COINS". Это необязательно, но защищает от ошибок, которые могут быть вызваны опечатками)

Далее мы создадим функциональный компонент с именем CoinContainer и импортируем useSelector и useDispatch из react-redux. Мы будем вызывать useSelector, чтобы получить наши монеты из нашего состояния, и мы установим диспетчеризацию на useDispatch, чтобы использовать ее для наших вызовов действий.

Отлично, теперь нам просто нужно принести нам несколько монет!

Как мы можем сделать запрос на выборку с этой текущей настройкой?

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

Обратите внимание, как мы импортируем и отправляем в наш диспетчер функцию с именем fetchCoins. Мы собираемся создать эту функцию в отдельном файле с именем coinActions.js. Это то, что в Redux называется создателем действия, функцией, которая возвращает объект действия для нашей отправки.

Все выглядит хорошо. Мы настраиваем наш запрос на выборку по URL-адресу нашего API, а затем отправляем данные, возвращенные обратно в действие SET_COINS в нашем редюсере. Мы должны ожидать, что теперь сможем отображать данные о монетах, верно?

Не совсем. Запросы на выборку возвращают нечто, называемое обещанием. Объект Promise — это объект, представляющий некоторое значение, которое будет доступно позже. Мы можем получить доступ к этому значению, когда оно «разрешится», сцепив функции then() с нашей функцией выборки. Затем мы можем взять этот ответ и преобразовать его в JSON.

Несмотря на это, наша функция выборки все равно вернется до того, как наше обещание будет разрешено, и наш код продолжит двигаться вниз по стеку, что означает, что наше возвращение отправит наш объект в нашу отправку до того, как у него появятся какие-либо данные для отправки.

Еще кое-что, что следует учитывать, это то, что мы всегда Redux должны отражать текущее состояние приложений, поэтому было бы оптимально отражать состояние между запросом данных и фактическим получением данных. Из этого следует, что мы бы 1.) хотели, чтобы функция отправляла действие, которое устанавливает наше состояние на загрузку при выполнении запроса, 2.) делала фактический запрос на выборку, а затем 3.) как только этот запрос разрешался, отправляла другую отправку добавьте данные в наше состояние (и, пока мы на нем 4.), отправка для обнаружения любых ошибок, которые могут возникнуть из нашего запроса, также была бы полезна).

Если бы только был способ!

Промежуточное ПО Thunk на помощь

Redux работает синхронно, запуская одно действие за другим. Для выполнения асинхронных запросов мы будем использовать библиотеку промежуточного программного обеспечения под названием Redux-Thunk. Преобразователь проверит, возвращает ли наше действие функцию или объект. Как мы помним ранее, обычно действие — это просто объект JavaScript. Теперь каждый раз, когда мы запускаем действие, оно будет проверяться с помощью redux-thunk, чтобы увидеть, возвращает ли оно объект или функцию, и если оно возвращает функцию, оно передает аргумент отправки вашей функции, которую вы затем можете использовать для отправки действий в подходящее время.

Чтобы использовать Thunk, нам нужно сначала установить redux-thunk и внести некоторые коррективы в наш импорт.

Мы импортируем в applyMiddleware и передаем его в качестве второго аргумента в наш createStore. наконец, мы передадим applyMiddleware(thunk), который мы импортировали из redux-thunk.

Давайте посмотрим, как можно переписать нашу функцию fetchCoins, используя логику Thunk. Помните, что все, что нам нужно сделать с нашей стороны, это вернуть функцию вместо объекта и ожидать, что эта функция будет передана в качестве аргумента. Мы связываем эти вызовы вместе, и Thunk позаботится о выполнении кода в нужное для нас время.

Хороший! Мы видим, что, используя этот шаблон, мы удовлетворяем всем требованиям правильного
действия выборки, которые мы изложили выше:

  • мы отправляем действие, чтобы установить наше состояние на загрузку
  • мы делаем запрос на получение
  • мы отправляем действие для обновления нашего состояния после разрешения нашего запроса
  • у нас есть третья отправка, чтобы поймать любые ошибки, если наш запрос не будет выполнен

Теперь мы должны быть подключены к нашему создателю действий, созданному с помощью Thunk, из нашего вызова useEffect, который мы сделали ранее, из функции, преобразованной с помощью Thunk, к нашему редюсеру и от нашего редуктора к нашему хранилищу и обратно к нашему компоненту!

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

Заключение

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

Спасибо за просмотр этого урока! Я приветствую ваши отзывы.

Удачного кодирования!