![](/img/trans.png)
[英]How to dispatch multiple actions in ngrx/effect (redux-observable)?
[英]How to dispatch multiple actions from redux-observable?
我想從一個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 }
})
})
}
現在,我還想刷新列表( GET_CATEGORIES_REQUEST
),而不僅僅是調度ADD_CATEGORY_SUCCESS
。 我嘗試了很多東西,但總是得到
動作必須是普通對象。 使用自定義中間件進行異步操作
例如:
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
等
問題是在你的switchMap
你返回一個Promise,它本身解析為一個concat Observable; Promise<ConcatObservable>
。 switchMap
運算符將監聽Promise,然后按原樣發出ConcatObservable,然后由redux-observable提供給store.dispatch
。 rootEpic(action$, store).subscribe(store.dispatch)
。 由於調度Observable沒有意義,這就是為什么你會得到錯誤,即操作必須是普通對象。
你的史詩發出的必須始終是普通的舊JavaScript動作對象,即{ type: string }
(除非你有其他中間件來處理其他事情)
由於Promises只發出一個值,我們不能用它們發出兩個動作,我們需要使用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
或exhaustMap
。 在這個非常具體的情況下,我們選擇哪一個並不重要,因為我們將它應用於(包裝Promise)的Observable將只發出一個值然后complete()。 也就是說,理解這些運算符之間的區別至關重要,因此絕對需要一些時間來研究它們。
我將使用mergeMap
(再次,在這種特定情況下無關緊要)。 由於mergeMap
希望我們返回一個“流”(一個Observable,Promise,iterator或者數組),我將使用Observable.of
創建一個我們想要發出的兩個動作的Observable。
Observable.from(response)
.mergeMap(() => Observable.of(
{ type: ADD_CATEGORY_SUCCESS },
{ type: GET_CATEGORIES_REQUEST }
))
這兩個動作將按照我提供的順序同步和順序發出。
我們還需要添加錯誤處理,因此我們將使用RxJS中的catch
運算符 - 它與Promise上的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 }
))
把它們放在一起,我們會有這樣的東西:
const addCategoryEpic = action$ => {
return action$.ofType(ADD_CATEGORY_REQUEST)
.switchMap((action) => {
const db = firebase.firestore()
const user = firebase.auth().currentUser
const response = db.collection('categories')
.doc()
.set({
name: action.payload.name,
uid: user.uid
})
return Observable.from(response)
.mergeMap(() => Observable.of(
{ type: ADD_CATEGORY_SUCCESS },
{ type: GET_CATEGORIES_REQUEST }
))
.catch(err => Observable.of(
{ type: ADD_CATEGORY_ERROR, payload: err }
))
})
}
雖然這可以解決您的問題,但是多個Reducer可以從同一個單一操作中進行狀態更改,這通常是應該做的事情。 順序發出兩個動作通常是反模式。
也就是說,這在編程中很常見,這不是絕對的規則。 肯定有時候單獨行動更有意義,但它們是例外。 您可以更好地了解這是否是特殊情況之一。 請記住它。
為什么使用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 }
})
})
}
我發現我應該使用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 }
})
})
}
您也可以嘗試在史詩中使用商店
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 }
})
}
對於RxJs6:
action$.pipe(
concatMap(a => of(Action1, Action2, Action3))
)
請注意,我們有concatMap
, mergeMap
和switchMap
可以完成這項工作,差異: https : switchMap
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.