Усовершенствование опыта разработки



Общаясь с коллегами из разных компаний, я заметил, что очень многие команды обожают NgRx. Во время наших дискуссий меня не покидала мысль:


«Как было бы хорошо меньше сопровождать код, а больше следовать гайдам написания, чтобы облегчить последующее масштабирование».


Это побудило меня начать изучение различных методик рефакторинга Redux. Я смотрел видео, читал статьи и документы Redux. В конце-концов, я написал плагин для NgRx под названием NgRx Ducks


Цели


В основном ngrx-ducks создан для упрощения работы с NgRx.


  1. Упрощенная обработка экшенов с помощью Decorator API, который уменьшает объем кода, который необходимо написать, чтобы настроить как редюсер (Reducer), так и экшен креатор (Action creator). NgRx Ducks генерирует экшен-креаторы и функции редюсера автоматически.
  2. Меньше технического сопровождения, так как вам не нужно писать энумераторы (enums) или юнион-типы (union types).
  3. Более ясная логика приложения обеспечивается благодаря интуитивно понятному типизированному 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