Redux Observables:将 epics 用于相同的操作但不同的过滤器?

Redux Observables: Separate epics for same actions but different filters?

我是 redux 可观察对象的新手:https://github.com/redux-observable/redux-observable

我有一个简单的用例,我想根据用户的 signedIn 状态做两件不同的事情。

  1. 如果已登录,请添加 rsvp
  2. 如果没有登录,显示登录模式

我的 app/redux/epics/addRSVP.js 文件中有这个:

import 'rxjs';
import * as scheduleActions from '../ducks/schedule';

export default function searchUsers(action$, store) {
  return action$.ofType(scheduleActions.ADD_RSVP)
    .filter(() => store.getState().user.signedIn)
    .map(action => scheduleActions.rsvpAdded(action.payload));
};

我的问题是,我是否应该为已注销的用例创建另一个 app/redux/epics/preventRSVPIfNotSignedIn.js 史诗?类似于:

import 'rxjs';
import * as authenticationActions from '../ducks/authentication';

export default function searchUsers(action$, store) {
  return action$.ofType(scheduleActions.ADD_RSVP)
    .filter(() => !store.getState().user.signedIn)
    .map(action => authenticationActions.show());
};

或者有什么方法可以将两者放在同一个文件中吗?如果是前者,我觉得它最终会变成很多 epics。很高兴知道一般惯例是什么。

如果您不确定,请始终创建单独的 epics。以后测试和更改更容易。此外,这种方法几乎没有缺点 (performance-wise)。结合两个 epics 是一种在不知道是否真的有必要的情况下添加抽象。

此外,从看起来 来看,这两种(副作用)的作用域是不同的。我想说这是一个强有力的指标,表明在这里使用单独的 epics 是个好主意,并且会在未来得到更多证明。

也就是说,如果您确定您的史诗不会改变或变得更复杂(目前 if/else),我想也可以。


仔细一看,我猜你想做的是"if the user is not logged in, show im a login page and wait until (s)he is logged in and after a successful login fire the RSVP action"。如果这是您的用例,您可能需要查看 delayWhen。这可能是一个更好的解决方案,但它更像是 RxJS 的高级功能。当您对 redux-observables 更熟悉时,这可能是重构的好任务 :)

塞巴斯蒂安给出了很好的建议,通常我会将它们拆分并复制逻辑。但是,如果你真的经常这样做,你可以创建自己的抽象,这样你就不需要重复自己了。

您可以创建一个像 requireAuth 这样的帮助程序来执行您期望的操作,或者创建一个只有在具有有效授权时才应该接收这些操作的史诗。然后它将 return 一个包装它的新史诗。

// Helper to abstract the common auth requirement checks
// which will also dispatch authenticationActions.show()
// when they attempt to do something they can't
const requireAuth = (type, epic) => (action$, store) => {
  // matching actions which they have permission for
  const valid$ = action$
    .ofType(type)
    .filter(() => store.getState().user.signedIn);

  // matching actions they do NOT have permission for
  const invalid$ = action$
    .ofType(type)
    .filter(() => !store.getState().user.signedIn);

  return Observable.merge(
    epic(valid$, store),
    invalid$.map(action => authenticationActions.show())
  );
};

const searchUsersEpic = requireAuth(scheduleActions.ADD_RSVP, (action$, store) =>
  action$.map(action => scheduleActions.rsvpAdded(action.payload))
);

// You can then use requireAuth for any epics that require it
// as an example:
const searchMoviesEpic = requireAuth(scheduleActions.SEARCH_MOVIE, (action$, store) =>
  action$.mergeMap(action =>
    ajax(`/search/for/the/movie/${action.id}`)
      .map(resp => scheduleActions.searchMoviesFulfilled(resp))
  )
);

根据需要进行调整——但要小心,添加抽象会使您的代码库以后难以推理,或者当有人后来调整抽象而没有意识到它如何影响其他代码路径时引入错误。测试变得更加重要!