Что делает Redux? (и когда вы должны его использовать?)

Ломаете голову с Redux? Не беспокойтесь, вы не одиноки.

Я слышал от множества людей, что Redux является самым большим препятствием для написания приложений React, которые они хотят.

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

Зачем?

Лучший вопрос для начала: зачем нам вообще использовать Redux?

И ответ не «потому что все остальные в Интернете используют его». (Я не сомневаюсь, что многие используют его, но давайте глубже.)

Причина, по которой Redux полезна, заключается в том, что она решает проблему.

И нет, проблема, которую он решает, - это не «государственное управление». Это очень неопределенно. React уже занимается государственным управлением. Redux помогает управлять состоянием, но это не проблема, которую он решает.

Это о потоке данных

Если вы использовали React более нескольких минут, вы, вероятно, знаете о реквизитах и ​​одностороннем потоке данных. Данные передаются по дереву компонентов через реквизиты. Учитывая такой компонент:

Счетчик

count хранится в App состоянии будет передана в качестве опоры:

Передача реквизита вниз

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

Передача обратных вызовов

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

Надеемся, это обзор. (Если нет, вы должны остановиться здесь, пойти учиться React, создать пару небольших приложений и вернуться через несколько дней. Серьезно. Redux не имеет смысла, пока вы не поймете, как работает React.).

Слои и слои потока данных

Рано или поздно вы сталкиваетесь с ситуацией, когда в контейнере верхнего уровня есть некоторые данные, а для детей 4 уровня нужны эти данные. Вот скриншот Twitter, со всеми аватарами:

Пользовательские данные Twitter

Предположим, что аватар пользователя хранится как часть данных профиля, а App компонент верхнего уровня содержит пользователя. Чтобы доставлять user данные ко всем 3 Avatarкомпонентам, их user необходимо соткать через кучу промежуточных компонентов, которые не нуждаются в данных.

Отправка пользовательских данных до компонентов Avatar

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

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

Было бы неплохо, если бы компоненты, которые не нуждались в данных, не должны были видеть это вообще?

Подключить любые данные к любому компоненту

Это проблема, которую решает Redux. Он предоставляет компонентам прямой доступ к требуемым данным.

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

Подключение Redux к компонентам Avatar

Это реальность Redux.

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

Но эта вещь здесь, «подключить любые данные к любому компоненту», является основным событием. Если вам это не нужно, вам, вероятно, не нужен Redux.

AvatarКомпонент

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

import React from 'react';
import { connect } from 'react-redux';

const Avatar = ({ user }) => (
  <img src={user.avatar}/>
);

const mapStateToProps = state => ({
  user: state.user
});

export { Avatar };
export default connect(mapStateToProps)(Avatar);

Сам компонент не знает о Redux - он просто принимает userопору и отображает изображение аватара. mapStateToProps функция извлекает user из магазина Redux и отображает его на userопору. Наконец, connect функция - это то, что фактически передает данные из Redux через mapStateToPropsи в Avatar.

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

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

Когда добавлять Redux

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

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

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

Что дальше

Прочтите часть 2 этой серии, где мы погрузимся в детали Redux: как ее настроить, и как важные элементы подходят друг другу (действия и редукторы и магазины, о мой!).