Redux store redux-observable

Multiple store updates in a redux-observable epic

So, you’ve run into the “how do I dispatch multiple store updates within one epic” issue? Well, if you’re using Redux-observable, you’re in luck.

Background

When building a React app using Redux.js, you typically write epics to perform some action that in turn updates the store. I’m a big fan of RxJS, since I’ve used Reactive Extensions (on the .NET platform) for 10 years. When switching to javascript/typescript it was an easy decision to start using RxJS instead.

React, meet Rx

When writing epics it’s very nice to be able to use Rx code to handle multiple API requests, side effects et.c. in a concise way. Enter: redux-observable. By installing that package you can write your regular Rx code in any epic, which is really nice. Here’s an example:

export const getUserInfoEpic = (
  action$: Observable<UserInfoAction>,
  _state$: StateObservable<State>,
  { api }: { api: Api },
): Observable<UserInfoAction> =>
  action$.pipe(
    ofType(USERINFO),
    mergeMap(action => api.getUserInfo(action.id))
  );
);

In the code above, information about a user is retrieved when the USERINFO action is dispatched. The middleware picks it up and performs an api request, retrieving the user information.

Key feature

When using redux-observable, note the return type for your epic. It’s not a value (data), it’s an observable stream of data. This is key. That means you can return more than one value, and therefore automagically perform multiple store updates sequentially, in the correct order.

Without utilizing observable streams

If you were to do this ‘manually’, i.e. without using the full power of Rx, here’s an example with two ‘actions’ in one epic:

export const getUserInfoEpic = (
  action$: Observable<UserInfoAction>,
  _state$: StateObservable<State>,
  { api }: { api: Api },
): Observable<UserInfoAction> =>
  action$.pipe(
    ofType(USERINFO),
    tap(() => store.dispatch(setLatestUserRetrievalTimestamp()),
    mergeMap(action => api.getUserInfo(action$.id))
  );
);

What we want to do here is to dispatch two actions which update the store:

  1. Set the latest time stamp when a user was retrieved. This is done as a side effect using the tap operator.
  2. Update the user info after getting it from the server.

By utilizing observable streams

Now, dispatching an action is not the cleanest way to do this, but it does work. A nicer way is to utilize the fact that we return a stream. A stream can contain one value, or more than one value. By concatenating our changes in the order we want them applied to the store, redux-observable will do this for us automatically. Here’s how:

export const getUserInfoEpic = (
  action$: Observable<UserInfoAction>,
  _state$: StateObservable<State>,
  { api }: { api: Api },
): Observable<UserInfoAction> =>
  action$.pipe(
    ofType(USERINFO),
    mergeMap(() => {
        return concat(of(setLatestUserRetrievalTimestamp()),
        api.getUserInfo(action$.id));
    }
  );
);

This assumes of course that the return types for the observables you concat are the same. If not, use another operator, or map the result somehow. This is all implementation dependent.

The key is to return a stream of observables, which we do here with concat. Redux-observable then applies each item in the stream to the store, in order.

Summary

Using React together with RxJS and redux-observable lets you use the full power of observables. By returning a stream, you can return several store updates in one epic.


Posted

in

, ,

by

Tags: