如果你是 RxJS 的新手,这不是那么简单 :)
前面几件事:
运营商链
Epic 是一个函数,它接受一个动作流并返回一个动作流。行动进来,行动出去。您链接以转换匹配操作的函数称为操作符。链接运营商很像链接花园软管或电源线——价值从一个流向另一个。它也非常类似于仅链接常规函数,third(second(first()))
除了 Observables 有一个额外的维度time,因此运算符应用于流经它们的每个值。
因此,如果您说当您忽略源的值而只是将其映射到提供的值时,您首先映射到的事实变得毫无意义stream.mapTo(x).mapTo(y)
。x
.mapTo(y)
mapTo
如果改为使用map
,它可能会变得更加明显:
stream.map(value => 'a message').map(message => message + '!!!')
更清楚地说,这种操作符的链接是 RxJS,而不是特定于 redux-observable,它更像是使用惯用的 RxJS和少量胶水到 redux 的模式。
action$
是一个 Observable(技术上是 ActionsObservable)
参数action$
是动作的 Observable,而不是实际动作本身。action$.notification
也会如此undefined
。这就是人们通常使用美元符号后缀的原因之一,以表示它是这些东西的流。
考虑只有 2 个动作,而不是 3 个
您的示例向您展示了使用三个操作NOTIFICATION_DISPLAY_REQUESTED
和另外两个操作来显示和隐藏通知。在这种情况下,最初的意图动作基本相同,displayNotification()
因为它将在另一个之后同步调度。
考虑只有两个动作,一个用于“显示此通知”,另一个用于“隐藏此通知”。虽然这不是一个规则,但它通常可以简化您的代码并提高性能,因为您的减速器不必运行两次。
这就是您的情况(当然,您可以随意命名):
export const displayNotificationEpic = (action$, store) =>
action$.ofType(DISPLAY_NOTIFICATION)
.delay(3000)
.map(action => hideNotification(action.notification));
// UI code kicks it off some how...
store.dispatch(displayNotification('hello world'));
然后您的减速器将收到DISPLAY_NOTIFICATION
,然后 3 秒后HIDE_NOTIFICATION
(订购任何东西)。
此外,记住redux-observable docs也很重要:
记住:在 reducer 已经收到 Epics 之后,Epics 与正常的 Redux 调度通道一起运行。当您将一个动作映射到另一个动作时,您并没有阻止原始动作到达减速器;那个动作已经通过他们了!
解决方案
虽然我建议在这种情况下只使用两个动作(见上文),但我确实想直接回答你的问题!由于 RxJS 是一个非常灵活的库,因此有很多方法可以完成您的要求。
这里有一对:
一部史诗,使用 concat
该concat
运算符用于一次订阅所有提供的 Observable,仅在当前一个完成时才移动到下一个。它一次“耗尽”每个 Observable。
如果我们想创建一个发出一个动作的流,等待 3000 毫秒然后发出另一个动作,你可以这样做:
Observable.of(displayNotification(action.notification))
.concat(
Observable.of(hideNotification(action.notification))
.delay(3000)
)
或这个:
Observable.concat(
Observable.of(displayNotification(action.notification)),
Observable.of(hideNotification(action.notification))
.delay(3000)
)
在这种情况下,它们具有完全相同的效果。关键是我们将 应用于与delay
第一个不同的 Observable ——因为我们只想延迟第二个动作。我们隔离他们。
要在你的史诗中使用,你需要一个合并策略运算符,如mergeMap
,switchMap
等。这些对于学习非常重要,因为它们在 RxJS 中经常使用。
export const requestNotificationEpic = (action$, store) =>
action$.ofType(NOTIFICATION_DISPLAY_REQUESTED)
.mergeMap(action =>
Observable.concat(
Observable.of(displayNotification(action.notification)),
Observable.of(hideNotification(action.notification))
.delay(3000)
)
);
两种不同的史诗
另一种方法是创建两个不同的史诗。一个负责将第一秒映射到第二个,另一个负责隐藏前等待 3 秒。
export const requestNotificationEpic = (action$, store) =>
action$.ofType(NOTIFICATION_DISPLAY_REQUESTED)
.map(action => displayNotification(action.notification));
export const displayNotificationEpic = (action$, store) =>
action$.ofType(DISPLAY_NOTIFICATION)
.delay(3000)
.map(action => hideNotification(action.notification));
这是有效的,因为史诗可以匹配所有动作,甚至是其他史诗发出的动作!这允许干净的分离、组合和测试。
这个例子(对我来说)更好地证明了这个例子不需要两个意图动作,但可能有你没有提供的要求证明它是合理的。
如果这很令人困惑,我建议先深入研究 RxJS。教程、视频、研讨会等。这只是表面的,它要深入得多,但对于大多数坚持下去的人来说,回报是巨大的。