Простое клиентское приложение размером 500 пикселей с использованием React и Redux

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

Полный исходный код доступен здесь: https://github.com/dhavaln/react-500px

Не используйте Redux, пока у вас не возникнут проблемы с ванильным React — Дэн Абрамов, создатель Redux

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

Redux уже объяснялся много раз и во многих популярных и понятных форматах.

Так что, если вы еще не знакомы с Redux, очень рекомендую серию видео egghead.io от Дэна Абрамоваhttps://egghead.io/courses/getting-started-with- редукс

(А если вышеперечисленного недостаточно) Вы можете воспользоваться этой супер-серией видео от Уэса Босаhttps://learnredux.com/

До этого я создал простой шаблон, который включает React, Redux, React-Router, React-Redux-Router и некоторые базовые конфигурации, чтобы я мог легко запустить многостраничное приложение React. Я использовал то же самое для клиентского приложения 500px.

Состояние приложения

Состояние приложения в основном представляет текущее состояние приложения с точки зрения пользовательского интерфейса и данных в чистом формате JSON. Итак, для этого демонстрационного приложения я использовал следующее состояние:

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

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

Логика приложения разделена на две функции Reducer, которые я объясню в следующем разделе.

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

http://redux.js.org/docs/recipes/reducers/NormalizingStateShape.html

Редуктор(ы)

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

> [4, 5, 6].reduce(function(acc, value){
    return acc + value; 
  }, 0)
> 15

Для этого приложения я создал два редуктора: один для главной страницы, а другой для страницы сведений о фотографии. Давайте проверим функцию редуктора домашней страницы:

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

Эта функция обрабатывает действия, связанные с фотографиями категории и поиском. Таким образом, пользователь может переключать категории, такие как «Популярные», «Редакторы», «Предстоящие» и «Поиск» по фотографиям с помощью этой функции.

Я использую много операторов Object.assign, поскольку стараюсь, чтобы мои редукторы были чистой функцией, подробнее здесь:

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

https://medium.freecodecamp.com/why-redux-needs-reducers-to-be-pure-functions-d438c58ae468#.8tj89hrrg

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

Давайте проверим редюсер Photo Detail, который помогает загружать и отображать Photo Detail.

Это довольно простая функция, которая обновляет полученную деталь фотографии в состоянии.

Действия

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

Всякий раз, когда запускается действие, Redux выполняет все редукторы и перестраивает новое состояние приложения.

Я разделил действия на фото, детали фото и поиск фото. Я просто размещу фото действия здесь, остальные два относительно одинаковы и просты для понимания:

Как мы видим, я экспортирую только loadPhotos, так как это единственное действие, которое я хотел сделать видимым для компонентов, а остальные функции используются внутри через Redux-Thunk (промежуточное ПО Redux я расскажу в отдельном разделе). Он работает как Action Creator и с помощью Redux-Thunk отправляет несколько действий.

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

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

Home — это компонент пользовательского интерфейса React, который отображает все фотографии в сетке фотографий. Я использую метод жизненного цикла componentDidMount, который срабатывает вскоре после загрузки компонента в браузере, чтобы инициировать действие загрузки фотографии.

Я использую свойства для вызова действия, давайте проверим, как я внедрил действия и состояние в этот компонент.

Прежде чем я объясню иерархию компонентов, поскольку я использую React-Router, вот конфигурация маршрутизатора.

Корневой компонент — это приложение, а компонент домашней (обслуживающей /) страницы — это домашняя страница.

App является компонентом-оболочкой для Main, и я внедрил в него состояние приложения и действия, которые будут переданы в Main и другие дочерние компоненты.

Самый простой способ передать свойства дочернему компоненту — использовать React.cloneElement(this.props.children, this.props) в функции рендеринга.

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

Промежуточное ПО Redux-Thunk

Я запустил приложение без промежуточного программного обеспечения, настроенного с помощью Redux, поскольку у меня были статические данные, которые я использовал для рендеринга фотографий. Но как только я интегрировался с 500px API, я понял, что без использования какого-либо промежуточного программного механизма он не будет хорошо работать.

Redux-Thunk я нашел простым в использовании и достаточно хорошим для текущего приложения. Есть и другие варианты, такие как Redux-Saga (который умело использует генераторы ES6), но я оставлю его для следующих приложений.

В этом видео от ReactCast объясняется, как легко написать собственное промежуточное ПО Redux.

https://www.youtube.com/watch?v=T-qtHI1qHIg

Полный исходный код приложения доступен здесь: https://github.com/dhavaln/react-500px

Нашли этот пост полезным? Пожалуйста, нажмите кнопку ❤ ниже! :)