首先,请注意 concat 运算符在 RxJS 6 中已弃用(这将是从 rxjs/operators 导入的运算符)。但是,用于创建 observable 的函数 concat 未 被弃用(这将是从 rxjs 导入的函数)。我建议您使用未弃用的运算符。
其次,您当前的方法存在几个问题。
action$.pipe(
ofType(START_CONTINUE_SESSION),
concat(
...
上述过滤匹配类型“START_CONTINUE_SESSION”的操作并允许它们传回 Redux 存储。这是因为concat 运算符让源事件通过并等待前一个可观察对象完成在开始下一个可观察对象。然而,因为 redux-observable 动作流永远不会完成,concat 应该永远不会开始下一个 observables!查看旧 RxJS 文档中的以下大理石图:
如图所示,源事件通过。使用 redux-oberable,这意味着您的“START_CONTINUE_SESSION”操作将陷入永无止境的重复循环。
即使动作流结束并且concat 将开始下一个可观察对象,还有其他问题:
...
iif(
() => state$.value.foo === true,
EMPTY, // I've assumed that this is equivalent to `empty()`
action$.pipe(
filter(() => state$.value.foo === true),
),
),
...
您首先检查商店中foo 的当前值。如果它的值为true,则不再发出任何内容(仅在此特定步骤中),接下来将开始from。如果它的值为false,则创建一个新的动作流订阅。对于调度的每个未来操作,这将检查存储中 foo 的当前值。当它的值最终变为true 时,流入的动作(可以是任何动作!)允许传回 Redux 存储。但请注意,此订阅永远不会结束!同样,只要foo 仍然是true,你就会有一个永无止境的动作循环进出 Redux 存储区。
您应该订阅state$,而不是订阅action$ 并检查状态。下面的例子有点不同,但我认为它展示了一种实现目标的方法。这将等待初始操作 (START_CONTINUE_SESSION),然后等待状态具有 foo === true,然后将操作和状态都发送到 mergeMap,您可以像往常一样处理它(获取、调度其他操作等)。 )。如果您不需要状态副本,则可以忽略它。
export const epic = (action$, state$) =>
action$.pipe(
ofType(START_CONTINUE_SESSION),
withLatestFrom(state$),
exhaustMap(([action, state]) =>
state.foo === true
? of([action, state])
: state$.pipe(
mergeMap(state =>
state.foo === true
? of([action, state])
: empty()
),
first(),
)
),
mergeMap(([action, state]) =>
// here we have triggering action and state with foo === true
from(fetch(...)).pipe(
// ... do important stuff here
)
),
)
至于响应初始操作(START_CONTINUE_SESSION),我在上面的示例中选择了exhaustMap。可能的替代方案包括concatMap、mergeMap 和switchMap。您应该选择最适合您的用例的运算符:
-
concatMap - 监听所有操作并按顺序运行多个工作流。
-
exhaustMap - 侦听第一个操作并等到您完成工作流程后再接受另一个操作。
-
mergeMap - 监听所有操作并并行运行多个工作流。
-
switchMap - 监听所有动作,但一次只运行一个。收到新操作时取消任何以前的工作流程。