Angular-redux в примерах
Один из вопросов, который встает перед разработчиком более менее сложного приложения — как реализовать управление изменяющимися данными, которые нужны в разных частях приложения.
Сначала я реализовывала эту задачу через сервис с Observable. Этот подход подробно описан в посте Angular 2 сервис с observable.
Но в какой-то момент ощутилась потребность в общем хранилище данных, и тогда мы задумались об использовании redux. В результате это еще дало более четкое разделение кода. Например, из компонентов ушла часть логики, что сделало их код чище.
То есть по сути получилось, что те сервисы — это были ‘велосипеды’, при наличии уже готового решения, в котором многие моменты под капотом. Да и вообще имеется документация и куча статей о сути redux подхода. Такое ощущение, что это что-то вроде английского, который признан международным языком, так что для того, чтобы понимать других разработчиков, желательно его знать))
Итак, на выбор есть две библиотеки @ngrx/store и angular-redux/store.
Они несколько различаются, хотя и там и там, конечно, же общие принципы. Про ngrx/store я подробно не расскажу, так как не использовала. Но по нему почему-то в интернете больше информации.
Например, с ходу нашлось видео, а вот тут есть пример реализации.
Про angular-redux было мало информации, когда мы с ним разбирались, да и сейчас почему-то этот вопрос не сильно освещен (или я плохо ищу), поэтому я решила написать. В силу своей компетенции, конечно.
Начать следует со знакомства с цепочкой взаимодействий redux сущностей:
Рисунки лениво взяты отсюда
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
Таким образом, итоговая схема однонаправленного изменения данных теперь такая:
Подключение redux в AppModule
Но для того, чтобы все это заработало нужно подключить в appModule все сторы, коих может быть много.
Думаю выше все понятно, единственное storeEnhancers — необходим для удобного отслеживания смены состояний с помощью Chrome Redux DevTools расширения.
А так выглядит комбинация редьюсеров:
И комбинация эпиков:
На этом все. Любые дополнения, правки, вопросы крайне приветствуются.
А еще совсем, не исключено, что скоро все перейдут на ngxs/store, который был написан разработчиком с целью создания более близкого ангуляр-подходам менеджера состояний.
Ника, благодарю за статью! Вы всё таки переехали на ngxs/store? Видел у вас уже статья была с его использованием front-nika.ru/ru/ngrx-effect-s-forkjoin/
Юрий, да переехали) И это было не так чтоб прям совсем просто) Рада, что статья была полезна!