Усовершенствование опыта разработки
Общаясь с коллегами из разных компаний, я заметил, что очень многие команды обожают NgRx. Во время наших дискуссий меня не покидала мысль:
«Как было бы хорошо меньше сопровождать код, а больше следовать гайдам написания, чтобы облегчить последующее масштабирование».
Это побудило меня начать изучение различных методик рефакторинга Redux. Я смотрел видео, читал статьи и документы Redux. В конце-концов, я написал плагин для NgRx под названием NgRx Ducks
Цели
В основном ngrx-ducks создан для упрощения работы с NgRx.
- Упрощенная обработка экшенов с помощью Decorator API, который уменьшает объем кода, который необходимо написать, чтобы настроить как редюсер (Reducer), так и экшен креатор (Action creator). NgRx Ducks генерирует экшен-креаторы и функции редюсера автоматически.
- Меньше технического сопровождения, так как вам не нужно писать энумераторы (enums) или юнион-типы (union types).
- Более ясная логика приложения обеспечивается благодаря интуитивно понятному типизированному API, который вы можете использовать в качестве внедряемого сервиса (injectable Service), который настраивает взаимодействие с Хранилищем (Store) за вас.
Как оно работает?
Эта библиотека работает как дополнительный слой над NgRx. Она автоматически создает экшен креаторы и функции редюсера за вас. Кроме того, вы получаете сервис, который можно использовать в своих компонентах. Этот Сервис предоставляет строго типизированный API, позволяющий как диспатчить (dispatching) экшены, так и селектить (selecting) данные из Хранилища.
Библиотека NgRx Ducks не меняет существующее поведение NgRx. Она только управляет Observable Хранилища и методом dispatch.
Так как NgRx Ducks опирается только на dispatch и тот факт, что Хранилище предоставляет Observable, NgRx Ducks остается совместимым с NgRx, даже если в релизе есть критические изменения.
Установка
NgRx Ducks легко интегрируется с существующим проектом NgRx. Достаточно установить пакет npm, и все готово к работе.
npm install @co-it/ngrx-ducks
# или
yarn add @co-it/ngrx-ducks
Decorator API
Основная идея NgRx Ducks — объединить экшен-типы и логику мутаторов (также называемые редюсерами). Поэтому был добавлен декоратор @Ducksify, аннотирующий обычный класс.
import { Ducksify } from '@co-it/ngrx-ducks';
@Ducksify<number>({
initialState: 0
})
export class Counter {}
Декоратор позволяет указать initialState вашего состояния. Передача initialState позволяет NgRx Ducks позже автоматически сгенерировать функцию редюсера.
Теперь можно размещать логику мутаций прямо внутри класса. Больше нет необходимости писать switch-case-выражения. Вместо этого мы создаем маппинг между типом экшена и логикой мутации, используя декоратор Action.
import { Action, Ducksify } from '@co-it/ngrx-ducks';
@Ducksify<number>({ /* ... */ })
export class Counter {
@Action('[Counter] Increase by passed value')
increaseBy(state: number, payload: number): number {
return state + payload;
}
}
NgRx Ducks мапит каждый Экшен с соответствующей логикой мутации. Duck содержит всю необходимую информацию для генерации функции редюсера.
Функция редюсера
Функции редюсера автоматически генерируются с помощью NgRx Ducks. Фабрика reducerFrom создает справочную таблицу, чтобы сопоставить каждый экшен-тип с соответствующей логикой мутации.
import {
reducerFrom,
DucksifiedAction,
...
} from '@co-it/ngrx-ducks';
export function reducer(
state: number,
action: DucksifiedAction): number {
return reducerFrom(Counter)(state, action);
}
Вам все еще нужно оборачивать reducerFrom внутри экспортируемой функции для корректной работы с компилятором AoT.
Сгенерированный редюсер необходимо добавить в ActionReducerMap с помощью NgRx (см. пример ниже).
Утиная сила!
Самое интересное еще впереди! Цель NgRx Ducks — упростить взаимодействие с Хранилищем. С автоматической генерацией функции редюсера все нормально, и поэтому давайте взглянем на динамический фасад, который также здесь создается.
Декоратор @Ducksify также заботится о регистрации вашей "утки" в качестве Сервиса в IoC-контейнере Angular. Это означает, что вы можете внедрять "утку" в свой компонент!
Диспатчинг экшенов в Хранилище
NgRx Ducks добавляет некоторые исправления в Duck, которые позволяют вам использовать простые вызовы функций вместо диспатчинга экшенов вручную. Вы получаете типизированный API внутри ваших компонентов.
import { Duck } from '@co-it/ngrx-ducks';
@Component({ /* ... */ })
export class CounterComponent {
constructor(@Inject(Counter) private _counter: Duck<Counter>) {
this.counter.incrementBy(42);
}
}
Вы внедряете Duck, а не сам Counter. Duck автоматически создает экшен креатор для incrementBy, который диспатчит экшен с переданным значением (payload). Благодаря динамическим типам TypeScript вы сразу получаете автозаполнение в IDE.
Короче говоря, NgRx Ducks автоматизирует весь процесс обработки Экшенов. Вы настраиваете экшен один раз в Duck, а после этого — просто используете типизированный, динамический фасад, сгенерированный и обновленный автоматически за вас.
Выборка данных из Хранилища
Duck использует селекторы NgRx для чтения данных из Хранилища. У каждой "утки" есть хелпер (helper) pick, который принимает селектор.
Давайте представим, что наш Counter зарегистрирован как функция NgRx с ключом "counter". Это позволило бы нам следующую настройку селектора.
import { createFeatureSelector, createSelector} from '@ngrx/store';
const visitCounter = createFeatureSelector<number>('counter');
const count = createFeatureSelector<number>(count => count);
@Component({ /* ... */ })
export class CounterComponent {
count$: Observable<number>;constructor(@Inject(Counter) private _counter: Duck<Counter>) {
this.count$ = this.counter.pick(count);
this.counter.incrementBy(42);
}
}
Вы можете посмотреть демо на stackblitz.io.
Duck позволяет осуществлять как триггеринг мутаций состояния, так и запрос данных из Хранилища. Вам нужен только один «утилизированный» Сервис, который настраивает удобный API для взаимодействия с Хранилищем.
И наконец
NgRx Ducks также легко интегрируется с Эффектами! НО мы это разберем подробно только в следующей статье.
Если же вам не терпится узнать об этом прямо сейчас, можете обратиться к комплексному примеру, который также доступен в stackblitz.io.
TL;DR
- NgRx Ducks работает как дополнительный слой поверх NgRx.
- Вы можете легко интегрировать NgRx Ducks в существующие проекты.
- Duck
…. автоматически генерирует экшен креаторы и функции редюсера
.… делает ненужными энумераторы экшенов и юнион-типы
…. это динамический фасад, который может быть внедрен в компонент
.… обеспечивает динамически-типизированные само-диспатчные (self-dispatching; sorry) экшены
…. позволяет читать данные из Хранилища с помощью pick-API
.… может использоваться Эффектами (см. Демо)
Вот и все, ребята!
Надеюсь, мне удалось убедить вас попробовать NgRx Ducks. Кроме того, мне интересно, что вы думаете об этой библиотеке. Если у вас есть какие-то идеи, — достаточно написать на GitHub