发送动作以响应取消

时间:2016-12-08 05:14:03

标签: rxjs redux-observable

我从redux-observable文档中的cancellation recipe开始,并希望将其扩展一下。

基本上我有一个场景,在触发取消后,使用takeUntil我想要发送另一个清理行动等等。

这是我到目前为止所提出的:https://jsbin.com/zemenu/195/edit?js,output

启动"获取用户信息"然后点击取消。我希望它按此顺序执行操作:
- USER/FETCH
- REQUEST/STARTED
- USER/CANCELLED
- REQUEST/CANCELLED

这适用于我现在设置的方式。但是,我必须依靠将dispatch传递到requestSequence函数,然后在finally中触发它。是否有一种更清洁的方法可以与可观察的操作员一起做到这一点?因此,当触发USER.CANCELLED时,某些最终操作将映射到requestSequence可观察对象内部。

启用Redux记录器,因此请检查控制台是否有所有操作。

1 个答案:

答案 0 :(得分:7)

而不是使用.takeUntil(),听起来好像你想使用.race(),这是相当恰当的命名。无论哪个流首先发出,都会获胜!另一个是取消订阅。

您需要根据需要重新构建一些内容才能使用它。您希望立即隔离您发出的第一个操作request.onStart(meta),与ajax请求Observable.fromPromise(apiCall(...args))分开。那么你想直接在ajax和取消之间竞争,所以你需要传递action$ ActionsObservable,因为你把所有这些都放在了帮手中。

https://jsbin.com/suvaka/edit?js,output

function requestSequence(apiCall, args, meta, action$) {
  return Observable.of(request.onStart(meta))
    .concat(
      Observable.fromPromise(apiCall(...args))
        .map((payload) => request.onSuccess(payload, meta))
        .catch((e) => Observable.of(request.onError(e, meta)))
        .race(
          action$.ofType(USER.CANCELLED)
            .map(() => request.onCancel(meta))
        )
    );
}

const fetchUserEpic = (action$, store) =>
  action$.ofType(USER.FETCH)
    .mergeMap(action =>
      requestSequence(
        userRequest, 
        [`/api/users/${action.payload}`],
        { activity: USER.FETCH, path: 'user' },
        action$
      )   
    );

附注:小心过早抽象,比如制作那些帮手。虽然你可能会在某些史诗中重复一些事情,但我发现抽象它可能会让以后更难以理解,特别是如果它是其他没有编写代码的人而且不是。 Rx大师。当然,只有您可以知道此建议是否适用于您和您的代码库。

对我而言,主要的一个令人困惑的问题是你必须传递给requestSequence的所有论据,对于许多人来说,当他们第一次遇到它时很难理解。如果你发现你的史诗非常普遍地完成同样的事情并且你想要重复使用,那么抽象整个史诗可能会更加清晰,并创建下面的userRequest API实用程序,你可以独立测试。

(未经测试,基本上是伪代码)

const createApiEpic = options =>
  action$ =>
    action$.ofType(options.on)
      .mergeMap(action =>
        Observable.of(request.onStart(meta))
          .concat(
            options.effect(action.payload)
              .map(payload => request.onSuccess(payload, meta))
              .catch(e => Observable.of(request.onError(e, meta)))
              .race(
                action$.ofType(options.cancel)
                  .map(() => request.onCancel(meta))
              )
          )
      );

const userRequest = id =>
  Observable.ajax.getJSON(`/api/users/${id}`);

const fetchUserEpic = createApiEpic({
  on: USER.FETCH,
  effect: userRequest
  cancel: USER.CANCELLED
});