Angular-redux в примерах

Один из вопросов, который встает перед разработчиком более менее сложного приложения — как реализовать управление изменяющимися данными, которые нужны в разных частях приложения.
Сначала я реализовывала эту задачу через сервис с Observable. Этот подход подробно описан в посте Angular 2 сервис с observable.
Но в какой-то момент ощутилась потребность в общем хранилище данных, и тогда мы задумались об использовании redux. В результате это еще дало более четкое разделение кода. Например, из компонентов ушла часть логики, что сделало их код чище.

То есть по сути получилось, что те сервисы — это были ‘велосипеды’, при наличии уже готового решения, в котором многие моменты под капотом. Да и вообще имеется документация и куча статей о сути redux подхода. Такое ощущение, что это что-то вроде английского, который признан международным языком, так что для того, чтобы понимать других разработчиков, желательно его знать))

Итак, на выбор есть две библиотеки @ngrx/store и angular-redux/store.

Они несколько различаются, хотя и там и там, конечно, же общие принципы. Про ngrx/store я подробно не расскажу, так как не использовала. Но по нему почему-то в интернете больше информации.

Например, с ходу нашлось видео, а вот тут есть пример реализации.

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

Начать следует со знакомства с цепочкой взаимодействий redux сущностей:

direct

Рисунки лениво взяты отсюда

Actions

Допустим мы инициируем в приложении получение данных юзера:

UserActions — класс, в котором определены все экшены, связанные с данными юзера. Каждый экшен — это js объект, который имеет одно обязательно поле — type, типа string и любые другие поля.

Также в данном классе заданы функции отправки экшена в редьюсер.

Это необходимо сделать, чтобы изменить состояние приложения:

А в некоторых случаях, помимо типа действия, отправляем еще и данные:

Полный код ниже:

Reducer

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

В коде ниже у стейта начальное значение initialState:

Таким образом, initialState сменяется на новое значение, то есть мы наконец-то обновили Store!

А все заинтересованные в новых данных подписчики получают обновленный результат через select:

Middleware

Но куда же нам девать http запросы, обработку ошибок и прочие, так называемые, side-effects, которых не должно быть в чистой функции, которой является редьюсер?

Тут на сцену выходит эпик — прослойка между экшеном и редьюсером, которую предоставляет redux-observable. В ngrx/store это называется Effect.

Ключевым моментом является прописанный тип действия. Именно в тот момент, когда оно осуществляется, и будет выполнен getUser эпик.

Надо сказать, очень много примеров с экшенам, где используется не mergeMap, а switchMap. Но в данном случае я не увидела смысла использовать switchMap, так как не требуется отмена действия, если оно снова диспатчится. А еще switchMap может привести к багам, подробнее можно прочитать в статье RxJS: Avoiding switchMap-Related Bugs

Таким образом, итоговая схема однонаправленного изменения данных теперь такая:

direct2

Подключение redux в AppModule

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

Думаю выше все понятно, единственное storeEnhancers — необходим для удобного отслеживания смены состояний с помощью Chrome Redux DevTools расширения.

А так выглядит комбинация редьюсеров:

И комбинация эпиков:

На этом все. Любые дополнения, правки, вопросы крайне приветствуются.

А еще совсем, не исключено, что скоро все перейдут на ngxs/store, который был написан разработчиком с целью создания более близкого ангуляр-подходам менеджера состояний.

Хотите быть в курсе новых статей?

  1. Юрий, да переехали) И это было не так чтоб прям совсем просто) Рада, что статья была полезна!