我想从一个redux-observable史诗派遣多个动作。我该怎么做?我最初是以
开头的const addCategoryEpic = action$ => {
return action$.ofType(ADD_CATEGORY_REQUEST)
.switchMap((action) => {
const db = firebase.firestore()
const user = firebase.auth().currentUser
return db.collection('categories')
.doc()
.set({
name: action.payload.name,
uid: user.uid
})
.then(() => {
return { type: ADD_CATEGORY_SUCCESS }
})
.catch(err => {
return { type: ADD_CATEGORY_ERROR, payload: err }
})
})
}
现在,我还想刷新列表(ADD_CATEGORY_SUCCESS
),而不仅仅是调度GET_CATEGORIES_REQUEST
。我尝试过很多东西,但总是得到
动作必须是普通对象。使用自定义中间件进行异步操作
例如:
const addCategoryEpic = action$ => {
return action$.ofType(ADD_CATEGORY_REQUEST)
.switchMap((action) => {
const db = firebase.firestore()
const user = firebase.auth().currentUser
return db.collection('categories')
.doc()
.set({
name: action.payload.name,
uid: user.uid
})
.then(() => {
return Observable.concat([
{ type: ADD_CATEGORY_SUCCESS },
{ type: GET_CATEGORIES_REQUEST }
])
})
.catch(err => {
return { type: ADD_CATEGORY_ERROR, payload: err }
})
})
}
或将switchMap
更改为mergeMap
等
答案 0 :(得分:5)
问题是你的switchMap
内部会返回一个Promise,它本身会解析为一个concat Observable; Promise<ConcatObservable>
。 switchMap
运算符将侦听Promise,然后按原样发出ConcatObservable,然后由redux-observable提供给store.dispatch
。 rootEpic(action$, store).subscribe(store.dispatch)
。由于调度Observable没有意义,这就是为什么你得到错误的原因,即操作必须是普通对象。
你的史诗发出的内容必须始终是普通的旧JavaScript动作对象,即{ type: string }
(除非你有其他中间件来处理其他事情)
由于Promise只发出一个值,我们不能使用它们发出两个动作,我们需要使用Observables。因此,让我们首先将我们的Promise转换为我们可以使用的Observable:
const response = db.collection('categories')
.doc()
.set({
name: action.payload.name,
uid: user.uid
})
// wrap the Promise as an Observable
const result = Observable.from(response)
现在我们需要将会发出单个值的Observable映射到多个动作中。 map
运算符不会执行一对多运算,而是我们要使用mergeMap
,switchMap
,concatMap
或{{1}之一}。在这个非常具体的情况下,我们选择哪一个并不重要,因为我们将它应用于(包装Promise)的Observable将只发出一个值然后完成()。也就是说,了解这些运营商之间的差异至关重要,所以绝对需要一些时间来研究它们。
我将使用exhaustMap
(同样,在特定的案例中并不重要)。由于mergeMap
期望我们返回&#34;流&#34; (一个Observable,Promise,iterator或数组)我将使用mergeMap
来创建我们想要发出的两个动作的Observable。
Observable.of
这两个动作将按照我提供的顺序同步和顺序发出。
我们也需要添加错误处理,因此我们将使用RxJS中的Observable.from(response)
.mergeMap(() => Observable.of(
{ type: ADD_CATEGORY_SUCCESS },
{ type: GET_CATEGORIES_REQUEST }
))
运算符 - 它与Promise上的catch
方法之间的差异很重要,但在外面这个问题的范围。
catch
把它们放在一起,我们会有这样的事情:
Observable.from(response)
.mergeMap(() => Observable.of(
{ type: ADD_CATEGORY_SUCCESS },
{ type: GET_CATEGORIES_REQUEST }
))
.catch(err => Observable.of(
{ type: ADD_CATEGORY_ERROR, payload: err }
))
虽然这可以解决你的问题,但是多个reducers可以从同一个动作进行状态更改,这通常是应该做的事情。顺序发出两个动作通常是一种反模式。
那就是说,这在编程中很常见,这不是绝对的规则。肯定有时候单独行动更有意义,但它们是例外。您可以更好地了解这是否是特殊情况之一。请记住这一点。
答案 1 :(得分:2)
为什么使用Observable.concat? SwitchMap从其回调函数等待包含值(在我们的例子中是动作数组)的Observable或Promise。所以不需要在返回的promise成功处理程序中返回Observable。 试试这个:
const addCategoryEpic = action$ => {
return action$.ofType(ADD_CATEGORY_REQUEST)
.switchMap((action) => {
const db = firebase.firestore()
const user = firebase.auth().currentUser
return db.collection('categories')
.doc()
.set({
name: action.payload.name,
uid: user.uid
})
.then(() => {
return ([
{ type: ADD_CATEGORY_SUCCESS },
{ type: GET_CATEGORIES_REQUEST }
])
})
.catch(err => {
return { type: ADD_CATEGORY_ERROR, payload: err }
})
})
}
答案 2 :(得分:1)
您也可以尝试在史诗中使用商店
const addCategoryEpic = (action$, store) => {
return action$.ofType(ADD_CATEGORY_REQUEST)
.switchMap((action) => {
const db = firebase.firestore()
const user = firebase.auth().currentUser
const query = db.collection('categories')
.doc()
.set({
name: action.payload.name,
uid: user.uid
})
return Observable.fromPromise(query)
}).map((result) => {
store.dispatch({ type: ADD_CATEGORY_SUCCESS });
return ({ type: GET_CATEGORIES_REQUEST })
})
.catch(err => {
return { type: ADD_CATEGORY_ERROR, payload: err }
})
}
答案 3 :(得分:0)
我发现我应该使用Observable.fromPromise
创建一个observable,然后使用flatMap
进行多项操作
const addCategoryEpic = action$ => {
return action$.ofType(ADD_CATEGORY_REQUEST)
.switchMap((action) => {
const db = firebase.firestore()
const user = firebase.auth().currentUser
const query = db.collection('categories')
.doc()
.set({
name: action.payload.name,
uid: user.uid
})
return Observable.fromPromise(query)
.flatMap(() => ([
{ type: ADD_CATEGORY_SUCCESS },
{ type: GET_CATEGORIES_REQUEST }
]))
.catch(err => {
return { type: ADD_CATEGORY_ERROR, payload: err }
})
})
}
答案 4 :(得分:0)
对于RxJs6:
action$.pipe(
concatMap(a => of(Action1, Action2, Action3))
)
请注意,我们有concatMap
,mergeMap
和switchMap
可以完成工作,区别在于:https://www.learnrxjs.io/operators/transformation/mergemap.html