Перейти к основному содержимому
Неофициальный Бета-перевод

Эта страница переведена PageTurner AI (бета). Не одобрена официально проектом. Нашли ошибку? Сообщить о проблеме →

Предшествующие решения

Redux имеет смешанное происхождение. Он похож на некоторые паттерны и технологии, но в то же время отличается от них в ключевых аспектах. Ниже мы рассмотрим сходства и различия.

Опыт разработчика

Дэн Абрамов (автор Redux) создал Redux во время подготовки к докладу на React Europe под названием "Горячая перезагрузка с путешествиями во времени". Его целью было создать библиотеку управления состоянием с минимальным API, но полностью предсказуемым поведением. Redux позволяет реализовать логирование, горячую перезагрузку, путешествия во времени, универсальные приложения, запись и воспроизведение действий без дополнительных усилий со стороны разработчика.

Дэн рассказал о своих замыслах и подходе в Changelog, выпуск 187.

Влияния

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

Flux

На Redux повлияли ключевые особенности Flux. Как и Flux, Redux предписывает сосредоточить логику обновления модели в определённом слое приложения ("хранилища" во Flux, "редюсеры" в Redux). Вместо прямого изменения данных кодом приложения, оба подхода требуют описывать каждое изменение как простой объект, называемый "действием".

В отличие от Flux, в Redux отсутствует концепция Dispatcher. Это связано с опорой на чистые функции вместо эмиттеров событий — чистые функции легко комбинировать без управления дополнительной сущностью. В зависимости от трактовки Flux, это можно считать либо отклонением, либо деталью реализации. Flux часто описывают как (state, action) => state. В этом смысле Redux сохраняет архитектуру Flux, но упрощает её благодаря чистым функциям.

Ещё одно важное отличие от Flux: Redux предполагает, что вы никогда не мутируете данные. Можно использовать обычные объекты и массивы для состояния, но мутация внутри редюсеров категорически не рекомендуется. Всегда возвращайте новый объект, используя оператор расширения или библиотеку для неизменяемых обновлений Immer.

Хотя технически возможно писать нечистые редюсеры с мутацией для особых случаев производительности, мы настоятельно не рекомендуем этого делать. Это сломает такие возможности, как путешествия во времени, запись/воспроизведение или горячая перезагрузка. Кроме того, неизменяемость обычно не вызывает проблем с производительностью в реальных приложениях — как показывает Om, даже при затратах на создание объектов выигрыш от избежания дорогих перерисовок и пересчётов значителен, поскольку чистота редюсеров точно указывает, что изменилось.

Создатели Flux, кстати, одобряют Redux.

Elm

Elm — функциональный язык программирования, вдохновлённый Haskell и созданный Эваном Чаплицким. Он реализует архитектуру "модель-вид-обновление", где обновление имеет сигнатуру: (action, state) => state. "Обновители" Elm выполняют ту же роль, что и редюсеры в Redux.

В отличие от Redux, Elm — язык, поэтому он может использовать преимущества строгой чистоты, статической типизации, встроенной неизменяемости и сопоставления с образцом (через case). Даже если вы не планируете использовать Elm, стоит изучить его архитектуру и поэкспериментировать. Существует интересный песочница с JavaScript-реализацией похожих идей. Здесь можно найти вдохновение для Redux! Приблизиться к статической типизации Elm можно через постепенную типизацию, например, с помощью Flow.

Immutable

Immutable — JavaScript-библиотека, реализующая персистентные структуры данных. Она производительна и имеет идиоматичный API для JavaScript.

(Хотя Immutable.js вдохновил Redux, сегодня мы рекомендуем использовать Immer для неизменяемых обновлений.)

Redux не зависит от того, как вы храните состояние — это может быть обычный объект, Immutable-объект или что-то ещё. Возможно, вам понадобится механизм (де)сериализации для создания универсальных приложений и гидратации состояния с сервера, но в остальном можно использовать любую библиотеку хранения данных, если она поддерживает неизменяемость. Например, Backbone не подходит для состояния Redux, так как его модели изменяемы.

Даже если ваша библиотека поддерживает курсоры, не используйте их в Redux-приложении. Всё состояние должно считаться доступным только для чтения, а для обновлений и подписки используйте Redux. Поэтому запись через курсор не имеет смысла. Если вам нужна декомпозиция состояния независимо от UI-дерева — используйте селекторы. Селекторы это композируемые функции-геттеры. Отличная реализация — reselect.

Baobab

Baobab — ещё одна библиотека с API для неизменяемого обновления обычных JavaScript-объектов. Хотя её можно использовать с Redux, это даёт мало преимуществ.

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

В отличие от Immutable, Baobab не использует специальные эффективные структуры данных, поэтому выигрыша в производительности нет. Проще использовать обычные объекты.

RxJS

RxJS — превосходный инструмент для управления сложностью асинхронных приложений. Существует даже библиотека, моделирующая взаимодействие человека и компьютера через взаимозависимые наблюдаемые.

Есть ли смысл использовать Redux с RxJS? Конечно! Они отлично работают вместе. Например, легко представить Redux-хранилище как наблюдаемое:

function toObservable(store) {
return {
subscribe({ next }) {
const unsubscribe = store.subscribe(() => next(store.getState()))
next(store.getState())
return { unsubscribe }
}
}
}

Аналогично можно комбинировать асинхронные потоки, преобразуя их в действия перед передачей в store.dispatch().

Вопрос: нужен ли Redux, если уже используется Rx? Возможно, нет. Несложно реализовать Redux на Rx. Некоторые утверждают, что это всего пара строк с использованием метода .scan() из Rx. Вполне возможно!

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

Отзывы

«Мне нравится, что вы делаете с Redux» Цзин Чэнь, создатель Flux

«Я спросил мнение о Redux во внутренней JS-группе Facebook, и его единогласно похвалили. Поистине потрясающая работа.» Билл Фишер, автор документации Flux

«Круто, что вы изобретаете улучшенный Flux, вообще не используя Flux.» Андре Стальц, создатель Cycle

Благодарности

  • The Elm Architecture за отличное введение в моделирование обновления состояния через редьюсеры;

  • Turning the database inside-out за переворот в сознании;

  • Developing ClojureScript with Figwheel за убедительную демонстрацию, что перезагрузка должна «просто работать»;

  • Webpack за Hot Module Replacement;

  • Flummox за обучение Flux без шаблонного кода и синглтонов;

  • disto за Proof of Concept горячей перезагрузки хранилищ;

  • NuclearJS за доказательство производительности этой архитектуры;

  • Om за популяризацию концепции единого атома состояния;

  • Cycle за демонстрацию, что функция часто является лучшим инструментом;

  • React за прагматичные инновации.

Отдельная благодарность Джейми Пэйтону за передачу имени npm-пакета redux.

Спонсоры

Первоначальная разработка Redux финансировалась сообществом. Знакомьтесь с выдающимися компаниями, сделавшими это возможным:

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