簡體   English   中英

如何從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運算符不會執行一對多,而是我們要使用mergeMapswitchMapconcatMapexhaustMap 在這個非常具體的情況下,我們選擇哪一個並不重要,因為我們將它應用於(包裝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))
)

請注意,我們有concatMapmergeMapswitchMap可以完成這項工作,差異: httpsswitchMap

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM