The good thing about the Redux + React ecosystem is you can always have a shot at something novel in the JavaScript landscape. Especially when it comes to the middleware of Redux to handle asynchronous requests, one will find a great selection of paradigms and implementations to choose from. In the beginning you often end up with a Promise based middleware to deal with asynchronous actions, but there is more in JavaScript like Observables and Generators to deal with asynchronous requests.
The tutorial will show you how to use Observables in Redux. Moreover it will give you an entry point into Reactive Programming. Keep in mind that it will only scratch the surface, but maybe afterwards you get the idea and want to give it a shot in some of your projects as well.
Reactive Programming gained a lot of attraction in the recent years. Netflix is one of the bigger companies using and representing it openly. The Rx family is well known for providing libraries for all kinds of platforms to make Reactive Programming happen - JavaScript is one of these platforms.
The Redux Observable RxJS: Going Epic with Reactive Programming tutorial uses redux-observable as middleware for asynchronous actions in Redux. It takes advantage of RxJS to provide observable streams. Observables are in contrast to Arrays collections over time - one can call them streams as well. Operators (filter, map, scan..) on Observables allow one to return a new stream while keeping the old stream untouched. That characteristic is called immutability, because we donât alter the previous object. We might be already familiar with the same functionality (filter, map, reduceâ¦) in plain JavaScript Arrays, but we can also apply them on streams over time. For instance one can even merge two streams into one stream to enable orchestration of the business logic.
Letâs scratch the surface of Reactive Programming with the usage of redux-observable. First we install the redux-observable middleware module.
Moreover redux-observable depends on Reactive Programming principles which are provided by RxJS .
We need to import rxjs explicitly to get all the operators (filter, map, scan..) on observables. The root file is sufficient for importing the module.
After weâve set up everything accordingly, we can start to replace the thunk approach observables. We do this from scratch and you can compare the solution afterwards with the thunk approach we used before.
First we need to authenticate our app with the SoundCloud API ( Whatâs an API? ).
The authentication process gets started by dispatching the auth function. We already do this in the Stream container component. No payload is necessary in the action creator, since we are only activating the whole authentication procedure.
Another important point is that the action type will not be represented in the reducer, because it only activates the process to authenticate. We are used to have actions + reducer pairs, but in redux-observable we can abuse the Redux middleware to trigger a whole process with one single action without pairing it to a reducer. The process itself will trigger multiple actions which our reducer will respect to store data.
Additionally we need to add the new action type in our constants.
src/constants/actionTypes.js
Now the concept of Epics comes into play. The core primitive of redux-observable are Epics. An Epic is a function which takes a stream of actions and returns a stream of actions.
Inside an Epic you can use the observable operators to create new streams or to orchestrate them. It is important to know that the outermost stream after all has to return an action for the Redux middleware.
Letâs setup our first Epic!
An Epic gives us an action$ observable as argument. The $ indicates that we are dealing with an observable. The observable has the helper function ofType to determine the type of the incoming action. Now we can listen to the AUTH action we triggered before.
Additionally we are using observable operators to chain a bunch of streams. Sc.connect initializes the connection to SoundCloud and returns a Promise which gets eventually resolved and returns a session object. We use Observable.from to turn a Promise into an Observable. Afterwards we would be able to catch errors on the stream:
Once the login of the SC.connect succeeds and the Promise resolves, it returns a session object. The output of a stream is the input of the next stream in the chain. Therefore we know that we get the session object to call setSession in shorthand which returns an action at the end of the Epic.
Letâs explain shortly the operators we used.
map - It maps one stream to another stream in a synchronous way.
mergeMap - It maps one stream to another stream in an asynchronous way. Commonly it is used to handle asynchronous requests. In non Reactive Programming map is used to map from a synchronous object to another synchronous object. One can use mergeMap to map from a synchronous object to an asynchronous object. For instance it can be used to map from an URL string to a Promise based HTTP request which gets resolved eventually.
But we didnât implement setSession yet!
Again we need to add the action type in our constants.
Now the authentication is finished. Letâs recap our first Epic driven process. We dispatch an action with a plain action creator. No reducer is responsible for the action type, but it kicks off our Epic. The Epic encapsulates the whole authentication process. At the end the Epic returns an action to set the session.
There could be a reducer listening to the action, but we donât need the session object for the sake of simplicity in the global state. But feel free to save the object in the global state on your own! Moreover we could implement error handling as well.
As we learned we can use actions to trigger Epics. Letâs use the last (unused) action to trigger two simultaneous Epics! One Epic to retrieve the user object and one epic to retrieve the list of tracks of the user. Both requests only need the session object, which we already send in the payload of the last action.
As you can see both Epics listen to the SESSION_SET action type. Afterwards we use again mergeMap and Observable.from to handle the API requests. We didnât implement both functions fetchMe and fetchSteam yet. Letâs implement them.
The requests should work now. Letâs fire actions to set the data in the global state object.
We can reuse the action creator we already have in place to set the tracks from a different file. We donât have to refactor the src/actions/track.js file, because it has only plain action creators. The whole file should look like the following.
Now we know the concept around Epics, but someone has to introduce them to the Redux store. The redux-observables module comes with a middleware function, which takes all combined Epics as argument. Afterwards the created middleware can be used to create the overall store middleware.
src/stores/configureStore.js
But one ingredient is missing: the combined rootEpic. Like combineReducer for multiple reducers in Redux, we can use the combineEpics helper from redux-observable to export a rootEpic for the Epic middleware. We simply import all Epics in our root actions file and export them as a combined rootEpic .
src/actions/index.js
The app should work again.
As I said in the beginning, the world around Reactive Programming is so much bigger. The ecosystem of React and Redux allows one to experiment with a lot of different paradigms. The redux-observable middleware is only a starting point for Reactive Programming. If you want to dive deeper into Reactive Programming, I can recommend the egghead.io courses about the topic and a great read by Andre Staltz .
You may encounter issues in that tutorial. Here you will find some references how to handle issues.
In case you want to know which versions npm installed during that tutorial, here a list of all npm packages in my package.json.