I'm still new with redux-observable so please excuse me if this is a stupid question.
Basically I need to await some Promises then dispatch multiple actions in an epic. But returning an array of actions just results in the error Actions must be plain objects. Instead, the actual type was: 'array'
, you can click the PING button from my CodeSandbox to test this.
I know that this can be solved using from() and pipe() and all that stuff, but I want to keep the format of try/catch with async/await if possible.
I've tried wrapping the action array with of() and from() but it's still the same error
Thanks in advance to anyone that can help me.
You need to have a consistent return type for your mergeMap
function.
Because it's an async function, this currently returns Promise<Action>
or Promise<Action[]>
, and there's no way you can emit two values from a promise / async function.
Then, your resulting observable will be an Observable<Action | Action[]>
, but redux-observable needs Action
without the array.
In my opinion, the best solution if you want to keep the async function is to have it consistently return Promise<Action[]>
, then add one additional mergeMap(array => array)
just after the original one to convert Observable<Action[]>
to Observable<Action>
:
mergeMap(async (action: any) => {
try {
// I need to call some APIs here
const res1 = await axios.get(
"https://jsonplaceholder.typicode.com/posts"
);
const res2 = await axios.get(
"https://jsonplaceholder.typicode.com/comments"
);
// Let's say an API throws an error
throw new Error("Mock error");
return [{
type: PONG,
data: res1.data
}];
} catch (error) {
// On error, we should dispatch 2 actions
return [
{
type: PONG,
data: []
},
{
type: HANDLE_ERROR,
error: error
}
];
}
}),
mergeMap(array => array)
Another possible solution, which is fun but maybe a bit more complex, is to use async generators instead. These are functions that can yield (kind of return) more than one value:
// Note the `*` meaning it's a generator function. Also can't be an arrow function
mergeMap(async function *(action: any) {
try {
// I need to call some APIs here
const res1 = await axios.get(
"https://jsonplaceholder.typicode.com/posts"
);
const res2 = await axios.get(
"https://jsonplaceholder.typicode.com/comments"
);
// Let's say an API throws an error
throw new Error("Mock error");
yield {
type: PONG,
data: res1.data
};
} catch (error) {
// On error, we should dispatch 2 actions
yield {
type: PONG,
data: []
};
yield {
type: HANDLE_ERROR,
error: error
};
}
})
This second solution I can't really recommend since async generators are quite hard to reason about, and they are not that well known in the community either.