Published on

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

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

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

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

Зачем?

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

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

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

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

Поток данных

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

Счетчик

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Компонент Avatar

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

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 prop и отображает изображение аватара. Функция mapStateToProps извлекает user из магазина Redux и отображает его на user prop. Наконец, функция connect - это то, что фактически передает данные из Redux через mapStateToProps и в Avatar.

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

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

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

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

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

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

Что дальше

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