这是一个使用redux-observable的工作示例。 https://redux-observable-playground-ykzsyp.stackblitz.io可以使用mergeMap和if / else语句实现我想要的功能,但是我希望使用Observable.filter
,因为这看起来更优雅。
我有一个史诗,当前正在调度一个动作,但是想基于使用单个流的过滤器来调度不同的动作。这是当前代码:
const streamEpic = (action$) => action$.pipe(
ofType('START_PLAYBACK_STREAM'),
switchMap(playbackStream$),
filter((playEvent) => playEvent.type === 'LOAD')),
map((playEvent) => loadActionCreator(playEvent.fileName))
// how can I filter on other types?
// and dispatch other actions?
);
我已经看到许多使用单个流和过滤器来映射不同动作的rxjs示例,例如:
playbackStream$
.filter((playEvent) => playEvent.type === 'LOAD'))
.map((playEvent) => loadActionCreator(playEvent.fileName));
playbackStream$
.filter((playEvent) => playEvent.type === 'START'))
.map((playEvent) => startActionCreator());
playbackStream$
.filter((playEvent) => playEvent.type === 'STOP'))
.map((playEvent) => stopActionCreator());
我正在尝试通过Redux可观察到的方法来做同样的事情,但是没有运气。如果我使用tap,ignoreElements和store.dispatch,我可以使以下内容起作用,但我知道它是反模式的。
const streamLoadEvents = (action$, store) => action$.pipe(
ofType('START_PLAYBACK_STREAM'),
tap(() => {
playbackStream$
.filter((playEvent) => playEvent.type === 'LOAD'))
.map((playEvent) => store.dispatch(loadActionCreator(playEvent.fileName)));
playbackStream$
.filter((playEvent) => playEvent.type === 'START'))
.map((playEvent) => store.dispatch(startActionCreator()));
playbackStream$
.filter((playEvent) => playEvent.type === 'STOP'))
.map((playEvent) => store.dispatch(stopActionCreator()));
}),
ignoreElements()
);
我知道我也可以在map或switchMap之类的内容中使用switch或if / else语句,例如此处的答案:Redux-Observable multiple actions in single epic,但我想避免这样做,因为它相当不雅致,并且确实没有充分利用流运算符。答案在这里:https://stackoverflow.com/a/40895613/367766似乎让我更近了……
这里建议的方法是什么?您是可以给我指出的运算符或示例吗?谢谢!
答案 0 :(得分:0)
您是对的,如果/ else仅应作为最后的手段使用,可能有几种方法可以解决此问题,无论如何,这是我的看法:
有一个merge方法,可以看作if / else运算符的反应等效项:您为它提供了可观察对象,并且如果它们中的任何一个发出了,则所得的可观察对象将也发出自己的价值(AC)。当新动作通过时,我们将对其进行检查,并相应地选择一个新动作创建者(AC)。让我们看一些代码:
const filteredAction$ = action$.pipe(
ofType('START_PLAYBACK_STREAM'),
);
这里没什么有趣的,只是过滤掉我们不感兴趣的动作类型,然后
const operation$ = merge(
filteredAction$.pipe(
filter((action) => action.payload.type === 'LOAD'),
mapTo(loadActionCreator),
),
filteredAction$.pipe(
filter((action) => action.payload.type === 'START'),
mapTo(startActionCreator),
),
filteredAction$.pipe(
filter((action) => action.payload.type === 'STOP'),
mapTo(stopActionCreator),
),
).pipe(
startWith(startActionCreator)
);
在此,我们根据动作的有效负载来选择要使用的动作创建者(请注意,我正在关注flux standard for actions-动作的数据现在位于有效负载属性下,有一个很棒的library您可以使用它作为辅助。另外,您可能应该重命名“ type”属性以避免混淆)。
mapTo基本上告诉operation $流发出什么,您可以认为此表达式是将函数分配给某个变量以供以后使用。每次调用史诗时,我们都会在此处为其选择合适的动作创建者。 这里实际上不需要startWith(假设第一个动作始终包括 “ START”类型),并且仅出于语义目的。
最后但并非最不重要的是,史诗必须返回至少一个动作来进行分派:
return combineLatest(
operation$,
filteredAction$
)
.pipe(
map((arg) => {
const operation = arg[0];
const actionObject = arg[1];
return operation(actionObject);
})
)
两个流中的最新值组合在一起以形成后续动作:operation $(这将为我们提供适当的动作创建器函数)和filteredAction $(始终包含动作本身,我们可能需要一些数据)。 / p>