首页 > 解决方案 > 在 redux-observable 中完成一些异步操作后,如何对 redux 操作进行排序?

问题描述

语境

对于需要加载的不同页面元素,我有很多形式的操作:LOAD_XYZ, LOAD_XYZ_FAIL, 。LOAD_XYZ_SUCCESS

我经常希望react-router在某些项目加载后执行重定向(),但由于redux-observable不返回承诺,我无法在我的组件中执行重定向 - 但我必须使用reduxand push('...')from来执行此操作react-router-redux

不幸的是,这导致了很多重复,就像我拥有的​​一样,LOAD_XYZ以及LOAD_XYZ_REDIRECT_TO相同动作的版本,以及每个动作的两个史诗。

这开始让人感到浪费并且冗余过多

const fetchUserEpic = action$ =>
  action$.ofType(FETCH_USER)
    .mergeMap(action =>
      ajax.getJSON(`https://api.github.com/users/${action.payload}`)
      .map(response => fetchUserFulfilled(response))
    );

const fetchUserAndRedirectEpic = action$ =>
  action$.ofType(FETCH_USER_AND_REDIRECT)
    .mergeMap(action =>
      ajax.getJSON(`https://api.github.com/users/${action.payload}`)
      .concatMap(response => [fetchUserFulfilled(response), push(action.redirectTo)])
    );

问题

是否有某种模式/方法,我可以在某种异步请求完成后对要调度的操作进行排序,以避免冗余和必须实现同一史诗的多个版本。

例如,我希望有一个单独的REDIRECT_TO操作,我可以在加载项目后对其进行排序。

像这些(想象的)解决方案:

dispatch(fetchUser(...)).then(redirectTo("..."))
dispatchSequence(fetchUser(...), redirectTo("..."))

我知道redux-thunk可以做到这一点,但是我错过了所有 rxjs 运算符。

标签: javascriptreduxrxjsredux-observable

解决方案


我可能正在深入危险的地方,但我确实找到了一种方法来做到这一点。

首先,我们制作一个中间件,它将作为元数据添加到 action 中的完成 observable。

import { Subject } from "rxjs";

export const AddListenerMiddleware = store => next => action => {
  const forkedAction = Object.assign({}, action, { meta: { listener: new Subject()}});
  next(forkedAction);
  return forkedAction.meta.listener.asObservable();
};

由于这是 aSubject我们可以调用next它来发出一些值。创建一个notify助手允许我们通知任何订阅者操作​​何时完成(加上传递数据)。

// notify doesn't consume but just emits on the completion observable
const notify = (action, fn) => source => 
  Observable.create(subscriber => 
    source.subscribe(emmission => {
      if (action.meta && action.meta.listener) {
        action.meta.listener.next(fn(emmission));
      }
      subscriber.next(emmission);
    })
  );

const fetchUserEpic = action$ =>
  action$.ofType(FETCH_USER)
    .mergeMap(action =>
      ajax.getJSON(`https://api.github.com/users/${action.payload}`)
      .map(response => fetchUserFulfilled(response))
      .pipe(notify(action, response => response.data))
    );

现在我们的 dispatch 返回一个 observable,我们可以在它上面进行标准的 rxjs 操作。所以这最终成为可能:

dispatch(fetchUser(...))
    .subscribe(({id}) => dispatch(redirectTo("/" + id)))

免责声明:使用风险自负!


推荐阅读