发送响应取消的操作

Dispatch an action in response to cancellation

我从 redux-observable 文档中的 cancellation recipe 开始,想对其进行一些扩展。

基本上我有一个场景,在取消被触发后,使用 takeUntil 我想分派另一个动作来清理等

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

开始 "Fetch User Info" 然后点击取消。我希望它按以下顺序执行操作:
- USER/FETCH
- REQUEST/STARTED
- USER/CANCELLED
- REQUEST/CANCELLED

这按照我现在设置的方式工作。但是,我必须依靠将 dispatch 传递给 requestSequence 函数,然后在 finally 中触发它。有没有一种更简洁的方法可以只使用可观察的运算符来做到这一点?因此,当 USER.CANCELLED 被触发时,一些最终操作会映射到 requestSequence 可观察对象内部。

Redux 记录器已启用,因此请检查控制台以了解所有操作。

而不是使用 .takeUntil(),听起来您想使用 .race(),这个名字非常恰当。哪个流先发出,谁就赢了!另一个未订阅。

您需要对某些内容进行一些重组才能按需使用它。您希望将您立即发出的第一个操作(您的 request.onStart(meta))与 ajax 请求 Observable.fromPromise(apiCall(...args)) 分开。然后你想直接在 ajax 和取消之间进行比赛,所以你需要传入 action$ ActionsObservable,因为你在 helper 中拥有所有这些。

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

旁注:小心过早的抽象,比如制作那些类型的助手。尽管您可能会在某些 epics 中重复某些内容,但我发现将其抽象化会使以后更难理解,尤其是当代码不是由其他人编写且不是 Rx 大师时。当然,只有您自己知道这条建议是否适用于您和您的代码库。

对我来说主要的困惑点是你必须传递给 requestSequence 的所有参数,这对很多人第一次遇到它时很难理解。如果你发现你的 epics 非常非常普遍地做完全相同的事情并且你想重用,也许抽象整个史诗会更清楚,并在下面创建 API 实用程序,如 userRequest你可以独立测试。

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

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