Реактивные приложения на Angular/NGRX. Часть 3. Effects.

Igor Demyanyuk
4 min readFeb 28, 2018
Photo by timoshenkko

В третьей части серии статей мы поговорим про side-effect-ы:

Для того, чтобы понять что такое эффекты, рассмотрим следующую ситуацию. Мы создали стору для фильмов. Когда пользователь хочет внести новый фильм в список, мы получаем payload из вьюшки и установленный тип действия в компоненте контейнере. Тут все просто. Но как нам сделать, допустим, загрузку данных с сервера и комбинировать ее с redux? Посмотрим на следующий диалог двух разработчиков:

Легко! Сделаем сервис в котором создадим функцию получения данных http.get(...) , вызвав ее в компоненте и подписавшись на результат, когда данные придут отправляем action и заполняем данными стору. ГОТОВО!

— Хорошо, теперь посмотрим на наш компонент. В предыдущих статьях мы говорили, что компоненты контейнеры могут только принимать данные из сторы, передавать их stateless-компонентам и отправлять действия о каком-либо событии для того, чтобы мы могли отслеживать изменения. Они чистые! Это позволяет нам отключать холостые change detection-ы. И в данном случае наш компонент зависит от сервиса получающего данные (а не напрямую от сторы) там же располагается какая-то условная отправка action-ов не через UI.

И что!? Работает же! Оставим так!

— Окей, окей, давай оставим. Только другой разработчик решит заинжектить стору сразу в сервис, там же вызвать метод http.get(...) , там же подпишется и там же отправить action. И это тоже работает!

— Ну да! Правда… Я предпочел бы сделать это в компоненте!

— Вот именно! Все мыслят по разному в зависимости от уровня разработки, опыта и предпочтений. В итоге может получится Парк Аттракционов!

Именно поэтому нам нужно одно условное место, где мы будем хранить наши сайд-эффекты.

Side-effects

— Эффекты! Эффекты! Что это вообще такое?

— Отличный вопрос! Все имеют собственное мнение на этот счет и существует множество дискуссий в которые мне не очень хочется вдаваться. Поэтому сначала перечислим некоторые из них:

  • Обращение к серверу (ajax)
  • Запись\чтение local\session storage
  • Вывод в console
  • Изменение каких-либо глобальных переменных (больше относится к функциональному программированию, но и тут имеет место быть)

Если обобщить, то чаще всего это обращение к browser API и изменение каких-либо неконтекстных (глобальных) переменных, от которых зависит результат каких-либо функций или операций.

Ещё раз - когда мы обращаемся к серверу для принятия данных, записываем или берем в\из localstorage токен и т.д. — все это является side-эффектами

Ссылаясь на первую часть серии статей, хочу напомнить, что redux предполагает, что наш View, то есть компоненты, умеют только dispatch-ить (отправлять) action-ы. Поэтому все вычисления, запросы к бекенду и другим браузерным API — мы будем держать в наших эффектах. И примите Вы низкий поклон от верстальщиков, работающими с angular😇. Pure components — no confusion.

How does it works?

Итак, зачем они нужны мы разобрались. Теперь подумаем как их запустить или как они вообще работают.

Будем исходить из того, что у нас есть action-ы, которые мы отправляем из компонентов. И логично предположить, что если мы их как-то перехватим, то сможем отреагировать подходящим для нас образом. Именно так и устроен ngrx\effects.

Мы просто подписываемся на определенные action-ы.

Выше Вы можете увидеть что action сначала попадает в reducer и только потом в effect-ы, этот вопрос изводил многих моих знакомых, но важно знать порядок выполнения.

Когда action отправлен, наступает то самое время для наших асинхронных делишек 😈! Почему асинхронных? Потому что код будет выполнен только тогда, когда нашему observabl-у дадут волшебный пинок next-ом.

Каждый dispatch преобразуется в observable, который так же возвращает observable нового action-а. Другими словами по дефолту эффекты ожидают, что мы будем возвращать новый action observable. Но это конфигурируется в декораторе: dispatch:false

Practice!

Дефолтная установка:

npm install @ngrx/effectsили yarn add @ngrx/effects

Для демонстрации создадим небольшой сервис с методом, который будет выполнять http запрос и возвращать поток с данными.

Теперь реализуем непосредственно эффект. Тут я подразумеваю, что у вас есть два действия:

  • Инициализация загрузки (ее мы будем слушать в эффектах)
  • Успешный ответ сервера (описан в reducer-е)

Итак, мы инжектим класс Actions, представляющий все action-ы в виде Observable и наш сервис, где далее методом ofType подписываемся только на те action-ы, которые передаем в эту функцию. В данном случае мы “слушаем” LOAD_MOVIE.

Так же важно добавлять декоратор @Effect(), который обеспечивает метаданные для наших эффектов, регистрирующих их как новый источник action-ов.

Когда действие отправляется, оно также попадает в наш эффект, где мы можем взять payload (если он есть) и совершить какие-то действия. Тут мы заменяем начальный поток на новый с помощью switchMap и возвращаем его (функцией getMovie), так же мапимся, извлекая данные и возвращаем новый action. Все просто 😎.

Далее подключим сам модуль Effects и передадим наши эффекты как аргумент:

Отлично! Теперь мы понимаем механизм действия эффектов. В следующих статьях можем разобрать пару распространенных кейсов в эффектах, так что оставляйте комментарии, что бы Вы хотели разобрать.

Полезные ссылки:

Можете подписаться, чтобы не пропустить следующие статьи и не забываем делать claps— так я буду знать, что материал оказался полезным для Вас!

Источник: https://giphy.com

--

--