从 redux-observable 的史诗有条件地调度额外的动作

Posted

技术标签:

【中文标题】从 redux-observable 的史诗有条件地调度额外的动作【英文标题】:Dispatching additional actions conditionally from redux-observable epic 【发布时间】:2018-08-17 19:00:24 【问题描述】:

简而言之,我想设置一个执行请求并根据请求被满足或拒绝分派操作的史诗。然后我希望史诗可能会根据结果和当前状态调度其他操作。

第一部分很简单

const fetchFooEpic: Epic<ActionAny, RootState> = (action$, store) =>
  action$.pipe(
    ofType<ActionAny, ReturnType<typeof actions.foo.fetch>>(actions.foo.types.FETCH_ALL),
    switchMap(action =>
      ajax(
        method: 'GET',
        url: `$path/foo/$action.payload`,
        headers:  Authorization: store.getState().user.token 
      ).pipe(
        map((response) => actions.foo.fetchFulfilled(response)),
        catchError(error => of(actions.foo.fetchRejected(error)))
      )
    )
  )

但是我在引入另一个动作时遇到了麻烦,或者在混音中出现了空洞。我认为我想使用 mergeMap 并在不应该分派任何内容时清空,但我遇到了类型错误。

const fetchMissingRelations = (response: Foo[], state: RootState) => 
  const unknown: BarId[] = foo
    .map(foo => foo.barId)
    .filter(barId => !state.bar.entities[barId])
  return unknown.length
    ? actions.bar.fetch([...new Set(unknown)])
    : empty<never>()


const fetchFooEpic: Epic<ActionAny, RootState> = (action$, store) =>
  action$.pipe(
    ofType<ActionAny, ReturnType<typeof actions.foo.fetch>>(actions.foo.types.FETCH_ALL),
    switchMap(action =>
      ajax(
        method: 'GET',
        url: `$path/foo/$action.payload`,
        headers:  Authorization: store.getState().user.token 
      ).pipe(
        mergeMap((response) => of(
          actions.foo.fetchFulfilled(response),
          fetchMissingRelations(response, store.getState())
          // err: property 'type' is missing in 
        )),
        catchError(error => of(actions.foo.fetchRejected(error)))
      )
    )
  )

我最终进入https://github.com/redux-observable/redux-observable/issues/339,但是为空提供显式的 never 类型对我不起作用。

这就是问题所在(请随意停在这里),但这里有一些额外的背景说明我为什么要这样做,如果有人能提出替代方法,我将不胜感激:

我有几个状态切片,其中包含来自不同 API 端点的网络关系数据。在这种情况下,它是与内部和外部参与者的讨论。

当我获取讨论时,我想立即解析它们以查找对尚未处于该状态的参与者的任何引用,并将它们批量处理为填充缺失数据的请求(这样我就可以显示姓名、头像等在用户界面上)。如果所有信息都已经在本地可用,我不想要求任何东西。

我最初的计划是依赖连接的 React 组件,这些组件使用数据来检查生命周期事件 (componentDidMount/componentWillReceiveProps) 中缺少的引用实体并调度操作来填充数据,因此史诗可以坚持自己的领域而不关心还有什么需要更新的。

但是,这有点失控,因为该状态正在许多不同的地方使用,所有地方都需要在必要时进行检查和调度操作。尽管我喜欢将状态域分开,但我认为让处理讨论请求的史诗也分派更新其他内容的操作将占用更少的空间。原因是这将释放连接的组件以仅渲染或等待数据,而不是调度更新以填充缺失的引用。但我全心全意寻求更好的解决方案。

【问题讨论】:

这可能对您没有帮助,但我只是认为使用thunk 和基本的中间件会非常简单明了。不过真的不知道redux-observable 【参考方案1】:

empty 返回一个可观察对象,因此根据unknown.length,您的fetchMissingRelations 函数将返回(看起来是)一个动作或一个可观察对象。

你应该改变它,让它总是返回一个 observable:

const fetchMissingRelations = (response: Foo[], state: RootState) => 
  const unknown: BarId[] = foo
    .map(foo => foo.barId)
    .filter(barId => !state.bar.entities[barId])
  return unknown.length
    ? of(actions.bar.fetch([...new Set(unknown)]))
    : empty<never>()

您应该更改您的mergeMap 以考虑到这一点:

...
mergeMap((response) => concat(
  of(actions.foo.fetchFulfilled(response)),
  fetchMissingRelations(response, store.getState())
)),

【讨论】:

以上是关于从 redux-observable 的史诗有条件地调度额外的动作的主要内容,如果未能解决你的问题,请参考以下文章

如何在 redux-observable 史诗中链接 RxJS 可观察?

Redux-Observable 测试只读史诗

为啥我的 redux-observable 史诗没有被触发?

在使用 redux-observables 开始另一个史诗之前,如何等待不同的史诗完成并更新商店?

React redux-observable:以史诗般的方式进行顺序 API 调用

redux-observable 无法从发出的动作中捕获错误,错误会停止动作流