从 redux-observable 史诗中有条件地调度额外的动作
Dispatching additional actions conditionally from redux-observable epic
简而言之,我想设置一个史诗来执行请求并根据请求被满足或被拒绝来分派操作。然后我希望史诗可以根据结果和当前状态分派额外的动作。
第一部分很简单
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 和 empty,但是我收到类型错误。
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 type for empty 对我不起作用。
这就是问题所在(请随时在这里打住),但这里有一些额外的背景信息,说明我为什么要这样做,如果有人可以提出替代方法,我将不胜感激:
我有几个带有关系数据的状态切片,它们都来自不同的 API 端点的网络。在这种情况下,它是与内部和外部参与者的讨论。
当我获取讨论时,我想立即解析它们以获取对尚未处于该状态的参与者的任何引用,将它们分批放入请求中以填充缺失的数据(这样我就可以显示姓名、头像UI 等)。万一所有的信息都已经在本地可用了,我就不想再索取了。
我最初的计划是依靠连接的 React 组件使用数据来检查生命周期事件中缺少的引用实体 (componentDidMount/componentWillReceiveProps) 并调度操作来填充数据,因此 epics 可以坚持自己的领域,而不关心还有什么需要更新。
然而,这有点失控,因为该状态正在许多不同的地方使用,所有这些地方都需要在必要时进行检查和调度操作。尽管我喜欢将状态域分开,但我认为让处理讨论请求的 epics 也分派更新其他内容的操作将减少占用空间。原因是这将释放连接的组件来渲染或等待数据,而不是调度更新来填充缺失的引用。但我很期待更好的解决方案。
empty
return 是一个可观察对象,因此根据 unknown.length
你的 fetchMissingRelations
函数将 return 要么(看起来是)一个动作要么一个可观察的。
你应该改变它,让它总是 return 是一个可观察的:
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())
)),
简而言之,我想设置一个史诗来执行请求并根据请求被满足或被拒绝来分派操作。然后我希望史诗可以根据结果和当前状态分派额外的动作。
第一部分很简单
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 和 empty,但是我收到类型错误。
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 type for empty 对我不起作用。
这就是问题所在(请随时在这里打住),但这里有一些额外的背景信息,说明我为什么要这样做,如果有人可以提出替代方法,我将不胜感激:
我有几个带有关系数据的状态切片,它们都来自不同的 API 端点的网络。在这种情况下,它是与内部和外部参与者的讨论。
当我获取讨论时,我想立即解析它们以获取对尚未处于该状态的参与者的任何引用,将它们分批放入请求中以填充缺失的数据(这样我就可以显示姓名、头像UI 等)。万一所有的信息都已经在本地可用了,我就不想再索取了。
我最初的计划是依靠连接的 React 组件使用数据来检查生命周期事件中缺少的引用实体 (componentDidMount/componentWillReceiveProps) 并调度操作来填充数据,因此 epics 可以坚持自己的领域,而不关心还有什么需要更新。
然而,这有点失控,因为该状态正在许多不同的地方使用,所有这些地方都需要在必要时进行检查和调度操作。尽管我喜欢将状态域分开,但我认为让处理讨论请求的 epics 也分派更新其他内容的操作将减少占用空间。原因是这将释放连接的组件来渲染或等待数据,而不是调度更新来填充缺失的引用。但我很期待更好的解决方案。
empty
return 是一个可观察对象,因此根据 unknown.length
你的 fetchMissingRelations
函数将 return 要么(看起来是)一个动作要么一个可观察的。
你应该改变它,让它总是 return 是一个可观察的:
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())
)),