[idea] ngrx select pipe

Viktor Love
2 min readJul 16, 2021

--

ngrx богопротивен из-за особенностей использования. Давайте сделаем чутка красивее.

код до/после

Унылый код

В лучшем случае можно добиться такого:

Не такой унылый код

Но! Написав, кастомный пайп можно добиться вот такого чуда:

Или даже такого (с передачей дополнительного параметра внутрь селектора):

Т.е. конечно, так себе, но все равно лучше, чем стандартное решение.

Плюсы-минусы-особенности

✔️ Мы заметаем под ковер наличие rxjs. Это очень большой плюс, потому что store создан для того, чтобы решать все проблемы; а rxjs — только для сложных проблем.

✔️ Мы заметаем под ковер тот факт, что async pipe может выдать null. При работе со стором null быть не может. Нормальная типизация — это очень хорошо.

✔️ Мы убираем необходимость инжектить store и вызывать всякую каку (select)

⚠️ Если вдруг понадобится маппить данные в компоненте, то на компоненте придется создавать новый селектор. Как-то так: mappedSelector = createSelector(myOriginalSelector);

❌ Если вы вдруг используете более продвинутую работу со стором (select + filter + aggregate), то вам это не подойдет. Но я лично считаю это слабым минусом. Потому что небходимость в таких кейсах редка и это можно обойти.

Примитивная реализация

Базовый код, чтобы поддерживать этот сахар:

Ну тут все довольно очевидно, но проговорим:

  1. Пайп получает observable из стора, мемоизируя
  2. Делегирует работу async pipe
  3. Кастит тип с помощью !, убирая null — чистый артефакт async pipe, который не проявляется в реальности.

Сложная реализация

Здесь у нас уже есть поддержка параметризированных селекторов. В некоторых проектах очень нужно.

В моем случае параметризированный селектор выглядит так

export const makeGetCountAdjusted
= (adjustment: number)
=> (state: AppState)
=> state.count + adjustment;

И вот код pipe, позволяющего работать и с обычными селекторами, и с параметризированными:

Здесь мы продолжаем идею старой реализации, но

  1. Принимаем не просто селектор, а селектор или “фабрику” селектора. Различие между ними кодируем перегрузками функции transform
  2. Мы ввели хитровыделанный тип ForcedParameterlessSelector. С помощью такого типа мы добиваемся следующего. Если нам передана фабрика селекторов, но не передано аргументов, то компиляция падает. Это нужно, потому что по дефолту компилятор считает, что раз фабрика похожа на селектор, то это селектор и есть → и выбирает неправильную перегрузку.

--

--

Viktor Love
Viktor Love

Written by Viktor Love

Software Engineer from Ukraine. TypeScript, React, C#, Angular.

No responses yet