Multipe ajax calls in parallel and dispatching redux actions after success leads to “Uncaught Error: Actions must be plain objects”

Multi tool use
Multipe ajax calls in parallel and dispatching redux actions after success leads to “Uncaught Error: Actions must be plain objects”
I am new to rxjs and redux-observable. Trying to create an epic that allows me to do multiple parallel ajax calls and dispatches respective actions on success:
const loadPosters = (action$) => action$.pipe(
ofType(Types.LOAD_FILMS_SUCCESS),
switchMap(({ films }) =>
forkJoin(films.map(film =>
ajax
.getJSON(`https://api.themoviedb.org/3/search/movie?query=${film.title}`)
.pipe(
map(response => {
const [result] = response.results;
const poster = `http://image.tmdb.org/t/p/w500/${result.poster_path}`;
return Creators.savePoster(film, poster);
})
),
))
),
);
Creators.savePoster()
is an action creator for an action named SAVE_POSTER
. But, whenever i run my application, no such action is dispatched. Instead i get an error message in browser console:
Creators.savePoster()
SAVE_POSTER
Uncaught Error: Actions must be plain objects. Use custom middleware
for async actions.
Tried a simplified version without forkJoin
, sadly yielding the same result:
forkJoin
const loadPosters = (action$) => action$.pipe(
ofType(Types.INIT_SUCCESS),
mergeMap(({ films }) =>
films.map(film =>
ajax
.getJSON(`https://api.themoviedb.org/3/search/movie?query=${film.title}`)
.pipe(
map(response => {
const [result] = response.results;
const poster = `http://image.tmdb.org/t/p/w500/${result.poster_path}`;
console.log(Creators.savePoster(film, poster));
return Creators.savePoster(film, poster);
})
)
),
),
);
Appendix
Just for reference, I have another epic which does a simple ajax call which works fine:
const loadFilms = action$ => action$.pipe(
ofType(Types.INIT_REQUEST),
mergeMap(() =>
ajax
.getJSON('https://star-wars-api.herokuapp.com/films')
.pipe(
map(response => Creators.initSuccess(response))
),
),
);
1 Answer
1
The problem is that i don't return an Observable in my inner map. Changing:
return Creators.savePoster(film, poster);
to
return of(Creators.savePoster(film, poster));
makes it work.
By the way, if used with forkJoin
it's also possible (and in my case better) to take the mapped results after all requests resolved and dispatch a single action instead of multiple ones:
forkJoin
const loadPosters = (action$) => action$.pipe(
ofType(Types.INIT_SUCCESS),
mergeMap(({ films }) =>
forkJoin(
films.map(film =>
ajax
.getJSON(`https://api.themoviedb.org/3/search/movie?query=${film.title}`)
.pipe(
map(response => ({ film, response }))
)
),
)
),
mergeMap(data => {
const posters = data.reduce((acc, { film, response}) => ({
...acc,
[film.id]: `http://image.tmdb.org/t/p/w500/${response.results[0].poster_path}`,
}), {});
return of(Creators.savePosters(posters));
})
);
In fact this is my favorite solution so far.
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.