Angule

时间:2017-06-23 06:39:34

标签: angular redux rxjs redux-observable

我已经在一个项目上工作了大约10个月,它基于 Angular 4 + 和Redux(通过 angular-redux / store )。该项目大部分都取得了成功,自1月份开始投入生产,并没有遇到任何严重的问题。

但是......我已经逐渐对Redux感到失望,因为我认为这个问题至少令人烦恼,但可能对维护不利:初始状态null值,所以,我我想知道我做错了什么 - 或者如果这是Redux设计本身的问题

如何重现

以下是一个简单而好的例子:https://github.com/marinho/example-app

您只需克隆,npm install,然后运行npm run ng serve并在浏览器上加载 localhost:4200 。然后转到控制台并过滤“111”以查看两行:

11111 undefined
11112 null

由两个可观察的订阅打印,这两个订阅分别在myUndefinedmyObjects.country上观看值。第一个是无效密钥(未在全局状态下初始化),第二个是使用null初始化。

这些订阅的位置越精确:https://github.com/marinho/example-app/blob/master/src/app/component.ts#L15

为什么会发生

众所周知,在Redux上,必须configureStore具有初始状态。这种初始状态是一棵大树,所有可能的密钥都由它们的reducer初始化。在初始化时,没有执行任何操作,减少器都没有要解决的任何有效负载,因此在大多数情况下它们默认为null

直到这里,标准的Redux / Angular,AFAIK。

现在,我的两个订阅(myUndefined$myObjects$)会观察AnonymousSubject个实例,这些实例是 angular-redux / store 提供的通道在关键路径上更新的值(即@select(['myObjects', 'city'])表示<AppState>.myObjects.city)。

问题是,初始状态初始化时,所有已知状态键上都有新值 ,但几乎所有状态值都分配了null

解决方案还是黑客?

一旦有订阅者听到这些键中的任何一个,这样的null将被作为第一个值发出,这使得我需要在太多地方进行.filter(x => x !== null)或类似的空检查。那太难看了。

替代方案是使用 Epics (来自 redux-observables ),但它们必须设计得很好,结果会混淆脆弱而不是可靠的堆栈。

我的印象是,这不仅仅是 angular-redux / store 本身的问题,而且实际上是具有全局状态的副作用。

一个简单的解决方案可能是将 angular-redux / store 修复为仅在密钥不再是undefined时才开始发出值,因此,请避免使用{{1}初始化密钥而且根本不想初始化它们。但同样,如果这是Redux的问题,我不确定这是否是正确的方法。

那么,是否有人遇到过相同或有优雅解决方案?

1 个答案:

答案 0 :(得分:4)

在我看来,这是Redux固有的。从概念上讲,Redux是一种架构,它鼓励您将UI视为在任何给定时间对整个状态的纯计算。您的整个应用程序是一个大型状态机。因此,如果您将初始值设置为null,则该选择的第一个值将为null。如果您根本没有为字段设置初始值,则相应选择的第一个值将为undefined

通过这种方式,Redux可以“缩短时间”。在您的应用程序中并将其考虑在内。只有当前状态和下一个动作。这个基本假设是调试工具如此强大的原因。

我们可以让我们的Observables等到第一个非零值&#39;直到解雇,但这正是我个人使用Redux避免的那种时间依赖。我的感觉是,在Angular-Redux /商店库中进行此更改可能不是正确的调用,因为其他人可能并不期望它。我也不确定它会对时间旅行和其他调试工具产生什么影响。

那么在你的情况下,有哪些更清洁的方法来处理这些情况?

一般来说,我会考虑以下其中一项:

1)确保您的减速器始终处于初始状态。

这适用于您确实具有合理的密钥前置值的情况。因此,对于任何减速器控件state.myUndefined,您都可以

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

这样您就可以在reducer本身中对默认值进行编码。

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>;