typescript - Skickar ytterligare åtgärder villkorligt från redux-observable epic
Kort sagt, jag skulle vilja skapa en epic som utför en begäran och skickar en åtgärd baserat på om begäran uppfylls eller förkastas. Sedan skulle jag vilja att epiken kanske skickar ytterligare åtgärd(er) baserat på resultatet och det aktuella tillståndet.
Första delen är enkel
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)))
)
)
)
Men jag stöter på problem när jag försöker införa en annan åtgärd eller tomhet i blandningen. Jag tror att jag vill använda mergeMap och empty när inget ska skickas, men jag får typfel.
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)))
)
)
)
Jag hamnar i https://github.com/redux-observable/redux-observable/issues/339, men att ange typen never uttryckligen för empty fungerade inte för mig.
Det är frågan (känn dig fri att sluta här), men här är lite ytterligare sammanhang till varför jag försöker göra detta och jag skulle uppskatta om någon kan föreslå en alternativ metod:
Jag har ett par delstater med relationsdata som alla kommer över nätverket från olika API-slutpunkter. I det här fallet är det diskussioner med interna och externa deltagare.
När jag hämtar diskussioner vill jag omedelbart analysera dem för eventuella hänvisningar till deltagare som ännu inte finns i tillståndet, och samla dem i batchar för att fylla i den saknade informationen (så att jag kan visa namn, avatarer och liknande i användargränssnittet). Om all information redan finns lokalt vill jag inte begära någonting.
Min ursprungliga plan var att förlita mig på anslutna React-komponenter som använder datan för att kontrollera saknade refererade enheter i livscykelhändelserna (componentDidMount/componentWillReceiveProps) och skicka åtgärder för att fylla i datan, så att epikerna kan hålla sig till sina egna domäner och inte bry sig om vad som annars behöver uppdateras.
Men det börjar bli lite ohanterligt eftersom det tillståndet används på många olika ställen som alla behöver göra kontroller och skicka åtgärder när det behövs. Så mycket som jag gillar att hålla tillståndsdomänerna separata, tror jag att ha epics som hanterar diskussionsförfrågningar och även skickar åtgärder för att uppdatera annat kommer att ge ett mycket mindre avtryck. Resonemanget är att detta skulle frigöra de anslutna komponenterna att bara rendera eller vänta på data, istället för att skicka uppdateringar för att fylla i saknade referenser. Men jag är helt öppen för bättre lösningar.
Svar #1
empty returnerar en observable, så beroende på unknown.length kommer din fetchMissingRelations-funktion antingen att returnera (det som verkar vara) en action eller en observable.
Du bör ändra det så att det alltid returnerar ett observerbart:
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>()
}
Och du bör ändra din mergeMap för att ta hänsyn till det:
...
mergeMap(({response}) => concat(
of(actions.foo.fetchFulfilled(response)),
fetchMissingRelations(response, store.getState())
)),
