4

我从 redux-observable 文档中的取消配方开始,并希望对其进行一些扩展。

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

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

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

这以我现在设置的方式工作。但是,我必须依靠传入dispatch函数requestSequence,然后在finally. 仅使用可观察的运算符是否有更清洁的方法来做到这一点?因此,当USER.CANCELLED触发该操作时,某些最终操作将映射到requestSequence可观察对象内部。

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

4

1 回答 1

7

而不是 using .takeUntil(),听起来你想使用.race(),它的名字相当贴切。无论哪个流先发出,获胜!另一个已取消订阅。

您需要稍微重组一些东西才能根据需要使用它。您想将您立即发出的第一个操作,您的request.onStart(meta),与 ajax 请求分开Observable.fromPromise(apiCall(...args))。然后,您想直接在该 ajax 和取消之间进行竞争,因此您需要传入action$ActionsObservable ,因为您将所有这些都放在帮助程序中。

https://jsbin.com/suvaka/edit?js,输出

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
});
于 2016-12-08T06:11:53.160 回答