redux-observable is epic, goodbye redux-thunk and axios

Web development

Redux-observable is alternative way of performing async operations in JavaScript applications that use redux, apart from redux-thunk. And certainly more awesome. It uses RxJS underneath which I already wrote about briefly. Apart from bringing the power of functional reactive programming into my application I also see it as a way to better structure my application (while using epics). And I also discovered that for making requests to back-end I don’t need to import special library anymore because RxJS includes that too (Rx.Observable.ajax). So goodbye axios, you’ve been a very good friend to me the last couple of months, but I’ve moved from promises to observables.

What is it?

The core principle of redux-observable is this: When you dispatch a redux action that will do some async stuff, this action first goes through your normal reducer and after new state has been created, redux-observables sends this action into a stream and applies all your defined epics to it.

Epic is a function that takes an action and returns actions, for example in case of success and/or failure. It’s important to know all possible actions in your application goes through this stream. So you want to filter action to specific type and then perform async stuff possible based on action payload. Simple epic might look like this:

const searchEpic = (action$, store) =>
  action$.ofType('SEARCH')
    .map(action => action.payload)
    .debounceTime(400)
    .switchMap(query => 
      ajax.post('http://localhost:8080/search', {query})
        .takeUntil(action$.ofType('CANCEL_SEARCH')))
    .map(res => ({type: 'SEARCH_DATA', result: res.response}))

OK, it’s not that simple but if it were too simple than it won’t be worth using RxJS. 😀

Little cookbook

1 How do you dispatch sequence of actions from same epic?

const epic = (action$, store) =>
  action$.ofType('ACTION_TYPE')
    .mergeMap(res => Observable.of(actionCreator1, actionCreator2))

Read this for more.

2 How do you perform parallel requests like axios.all?

Observable.forkJoin(
    Observable.DOM.ajax.getJSON(`/users`),
    Observable.DOM.ajax.getJSON(`/likes`),
    (users, likes) => ({
      users: users.data,
      likes: likes.data
    })

Note that the last argument to forkJoin is optional and is a selector which parameters are ordered correspondingly to requests. In case if you miss axios.spread.

  • Andres Fulla

    Good move mate!

  • Shawn Stern

    Thank you so much for the forkJoin() example!

    I’ve been using Epics + ajax.getJSON() for a few months now but never ran into the situation of needing to do concurrent requests on one action until now and didn’t have any luck with the official docs/issue threads.

    Cheers!

    • Hi, I’m glad you found example useful! 🙂