Reduxで観察可能な叙事詩から複数のアクションをディスパッチしたいと思います。どうすればいいですか?私はもともと
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
などに変更します
問題は、switchMap
内で、それ自体がconcatObservableに解決されるPromiseを返すことです。 _Promise<ConcatObservable>
_。 switchMap
オペレーターは、Promiseをリッスンし、ConcatObservableをそのまま発行します。これは、redux-observableによって内部で_store.dispatch
_に提供されます。 rootEpic(action$, store).subscribe(store.dispatch)
。 Observableをディスパッチしても意味がないため、アクションはプレーンオブジェクトである必要があるというエラーが発生します。
エピックが出力するものは、常にプレーンな古いJavaScriptアクションオブジェクトである必要があります。つまり、_{ type: string }
_(他のものを処理するための追加のミドルウェアがない場合)
Promisesは単一の値しか発行しないため、2つのアクションを発行するためにそれらを使用することはできません。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
演算子は、1対多ではありません。代わりに、mergeMap
、switchMap
、concatMap
、またはexhaustMap
のいずれかを使用する必要があります。この非常に特殊なケースでは、適用しているObservable(Promiseをラップする)は単一の値のみを発行してからcomplete()を実行するため、どちらを選択してもかまいません。とはいえ、これらの演算子の違いを理解することは重要なので、必ず時間をかけて調査してください。
mergeMap
を使用します(ここでも、この場合は関係ありません特定のの場合)。 mergeMap
は「ストリーム」(Observable、Promise、iterator、またはarray)を返すことを期待しているので、_Observable.of
_を使用して、発行する2つのアクションのObservableを作成します。
_Observable.from(response)
.mergeMap(() => Observable.of(
{ type: ADD_CATEGORY_SUCCESS },
{ type: GET_CATEGORIES_REQUEST }
))
_
これらの2つのアクションは、私が提供した順序で同期的かつ順次に発行されます。
エラー処理も追加する必要があるため、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 }
))
})
}
_
これは機能し、質問に答えますが、複数のレデューサーが同じ単一のアクションから状態を変更できます。これは、ほとんどの場合、代わりに行う必要があります。 2つのアクションを順番に実行することは、通常、アンチパターンです。
とはいえ、プログラミングで一般的であるように、これは絶対的なルールではありません。別々のアクションを実行する方が理にかなっている場合もありますが、それは例外です。これがそれらの例外的なケースの1つであるかどうかを知るためのより良い立場にあります。覚えておいてください。
なぜ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 }
})
})
}
エピック内のストアを使用することもできます
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 }
})
}
Observable.fromPromise
を使用してオブザーバブルを作成してから、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 }
})
})
}
RxJs6の場合:
action$.pipe(
concatMap(a => of(Action1, Action2, Action3))
)
仕事をすることができるconcatMap
、mergeMap
、およびswitchMap
があることに注意してください。違いは次のとおりです。 https://www.learnrxjs.io/operators/transformation /mergemap.html