Начальное состояние Redux, полное нулевых значений в Angular

Я работал над проектом около 10 месяцев, который основан на Angular 4+ и Redux (через angular-redux/store). Этот проект был в основном успешным, он запущен в производство с января и не имеет серьезных проблем.

Но... Я постепенно разочаровался в Redux по одной проблеме, которую я считаю по крайней мере раздражающей, но потенциально плохой для обслуживания: начальное состояние с null Значения, поэтому я хотел бы выяснить, что я делаю не так - или, если это проблема дизайна Redux как таковая.

Как воспроизвести это

Вот простой и хороший пример этого: https://github.com/marinho/example-app

Ты просто клонируешь, npm install тогда беги npm run ng serve и загрузите localhost:4200 в браузере. Затем перейдите в консоль и выберите "111", чтобы увидеть две строки:

11111 undefined
11112 null

Это напечатано двумя наблюдаемыми подписками, которые соответственно следят за значениями на myUndefined а также myObjects.country, Первый является недействительным ключом (не был инициализирован в глобальном состоянии), второй был инициализирован с помощью null,

Более точное местоположение этих подписок находится здесь: https://github.com/marinho/example-app/blob/master/src/app/component.ts#L15

Почему так происходит

Как мы знаем, в Redux нужно configureStore с начальным состоянием. Такое начальное состояние представляет собой большое дерево со всеми возможными ключами, инициализированными их редукторами. К моменту инициализации не было выполнено никаких действий, ни один из редукторов не имел никакой полезной нагрузки для разрешения, поэтому они по умолчанию null в большинстве случаев.

До здесь стандарт Redux/Angular, AFAIK.

Теперь мои две подписки (myUndefined$ а также myObjects$) наблюдать AnonymousSubject экземпляры, которые представляют собой канал, предоставляемый angular-redux/store для испускания новых значений, обновляемых по ключевому пути (т.е. @select(['myObjects', 'city']) средства <AppState>.myObjects.city).

Проблема в том, что при инициализации начального состояния появляются новые значения для всех известных ключей состояния, но почти всем им присваивается null,

Решение или взлом?

Когда есть подписки, слушающие любой из этих ключей, такие как null будет выдано в качестве первого значения, что заставляет меня нуждаться в нулевых проверках, таких как .filter(x => x !== null) или подобное в слишком многих местах. Это действительно безобразно.

Альтернатива состоит в том, чтобы использовать Epics (из-за редукс-наблюдаемых), но они должны быть хорошо спроектированы, и результат пахнет хрупкой смесью с не очень надежным стеком.

У меня сложилось впечатление, что это проблема не только самого angular-redux/store, но и побочного эффекта глобального состояния.

Простое решение может заключаться в том, чтобы исправить angular-redux/store так, чтобы он начинал выдавать значения только тогда, когда ключ не undefined и так, избегайте инициализации ключей с null и просто предпочитаю не инициализировать их вообще. Но опять же, если это проблема Redux, я не уверен, что это будет правильный путь.

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

1 ответ

Решение

На мой взгляд, это присуще Redux. Концептуально Redux - это архитектура, которая побуждает вас думать о своем пользовательском интерфейсе как о чистом вычислении всего состояния в любой момент времени. Все ваше приложение - это один большой конечный автомат. Так что, если вы установите начальное значение null первое значение этого выбора будет null, Если вы не установите начальное значение для поля вообще, то первое значение соответствующего выбора будет undefined,

Таким образом, Redux "выравнивает" время в вашем приложении и вычеркивает его. Там только текущее состояние и следующее действие. Это фундаментальное предположение делает инструменты отладки такими мощными.

Мы могли бы сделать так, чтобы наши Observables "ждали до первого ненулевого значения" до срабатывания, но это именно та временная зависимость, которую я лично использую Redux, чтобы избежать. Я чувствую, что внесение этого изменения в библиотеку Angular-Redux/store, вероятно, не является правильным вызовом, учитывая, что другие люди, вероятно, не ожидают этого. Я также не уверен, как это повлияет на путешествия во времени и другие инструменты отладки.

Так что в вашем случае, какие есть более чистые способы справиться с этими ситуациями?

Обычно я хотел бы рассмотреть один из следующих:

1) убедитесь, что ваши редукторы всегда имеют начальное состояние.

Это работает для случаев, когда у вас действительно есть разумное предварительное значение для ключа. Так что для любого управления редуктором state.myUndefined, ты сделаешь

const INITIAL_VALUE = 'some-reasonable-value';
const myUndefinedReducer = (state = 'some-reasonable-value', action) => {
    // etc.
};

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

2) справиться с этим на выбор

Это работает для выборок, которые не связаны напрямую со свойством магазина (например, они являются результатом некоторых вычислений RxJS). Это также работает для случаев, когда начальное значение ключа является динамическим, например, это значение undefined пока вызов API не заполнит его или аналогичный.

Как вы уже заметили, это сводится к тому, чтобы делать фильтры в нескольких местах, которые могут быть многословными. Однако в вашем примере вы можете реализовать это более аккуратно, используя select$ и Transformer:

Некоторые файлы Utils:

export const skipNil = (obs$: Observable<any>): Observable<any> =>
    obs$.filter(v => v !== null && v !== undefined);

Component.ts:

@select$('myUndefined', skipNil) myUndefined$: Observable<any>;
@select$(['myObjects', 'country'], skipNil) country$: Observable<any>;
Другие вопросы по тегам