Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@vkarpov15
Created September 10, 2018 17:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vkarpov15/317f9882678018f64e571c2d463314fe to your computer and use it in GitHub Desktop.
Save vkarpov15/317f9882678018f64e571c2d463314fe to your computer and use it in GitHub Desktop.

Redux-Observable is a Redux middleware that allows you to filter and map actions using RxJS operators. RxJS operators like filter() and map() let you transform streams of actions just like how JavaScript's Array.prototype.filter() lets you transform arrays. In this article, I'll show you how to get started with redux-observable using scripts you can run from Node.js and provide a practical example of using Redux-Observable for HTTP requests with fetch().

Your First Epic

In redux-observable, an "epic" is a function that takes a stream of actions and returns a modified stream of actions. You can think of an epic as a description of what additional actions redux-observable should dispatch. An epic is analogous to the concept of a "saga" in redux-saga.

Before you write your first epic, you need to install redux-observable. This article assumes you already have Node.js and npm installed. To install redux-observable along with redux and RxJS, run the below command.

https://gist.github.com/2894d0eac0dbb2c60ad201954108fedf

The most fundamental function in the redux-observable API is the createEpicMiddleware() function. This function creates the actual Redux middleware you should pass to Redux's applyMiddleware() function. Below is an example of creating a middleware that transforms actions with type 'CLICK_INCREMENT' into actions with type 'INCREMENT'.

https://gist.github.com/658b619a1a12d8c3bc449eaf7dda3781

Say you dispatch an action with type 'CLICK_INCREMENT' to the above store as shown below.

https://gist.github.com/ca6a147c12174208ac8fd62a625595a2

Your filter() and map() calls will run, and redux-observable will dispatch an additional action of type 'INCREMENT'. Below is the output from the console.log() statement in the reducer() function.

https://gist.github.com/0f0702d2476ccec73fa89ec402c67940

Note that redux-observable dispatches an additional action: the 'CLICK_INCREMENT' action still gets through to the reducer. Epics add actions to the stream by default.

Asynchronous Dispatch

The above example serves as a simple introduction but doesn't capture why you would want to use redux-observable in the first place. What makes redux-observable so interesting is the ability to use RxJS' mergeMap() function to handle asynchronous functions. In other words, redux-observable is a viable alternative to redux-saga and redux-thunk. Below is an example of using redux-observable with a simple async function.

https://gist.github.com/92342c58bd747eb34d1cd7996fc59ac4

The countEpic() will now wait about 1 second before dispatching the 'INCREMENT' action.

https://gist.github.com/f6c2084d0d663c9a409b5e6cf74ef012

If you've read Mastering Async/Await, you know that this isn't the whole story with supporting async/await. What happens if your async function errors out? The below countEpic() will crash.

https://gist.github.com/9faf26db87b91c9b41386f80689dfad3

To handle errors, you should always put an RxJS catchError() at the end of your epic as shown below.

https://gist.github.com/d431b181dfe71f696d55fb4bf2f5096b

The countEpic() will now dispatch an action of type 'ERROR' with the error message.

https://gist.github.com/4e721428243b85c509fed7a3a9b405f3

Making an HTTP Request

The above examples are simple but not very realistic. Let's use redux-observable for a more realistic use case: making an HTTP request using node-fetch to get the current MongoDB stock price from the IEX API. To get the stock price, you need to make a GET request to the following URL.

https://gist.github.com/efc6a3e65444ef4a6f174f8c77926513

Since you can use async/await with mergeMap(), making an HTTP request with redux-observable is similar to the asynchronous dispatch example. Node-fetch returns a promise, so you can await on an HTTP request and then dispatch a new action with the result of the request.

In the below code, fetchEpic() fires off a GET request to the IEX API every time an action of type 'FETCH_STOCK_PRICE' comes through the system. If the request succeeds, fetchEpic() dispatches a new action of type 'FETCH_STOCK_PRICE_SUCCESS' with the stock price.

https://gist.github.com/75cba89dcdcfa7f1b4726e25d889ecb4

To glue fetchEpic() to Redux, the below reducer stores a map prices that maps stock symbols to prices. To store the stock price of MongoDB in Redux, the below reducer listens for actions of type 'FETCH_STOCK_PRICE_SUCCESS', not 'FETCH_STOCK_PRICE'.

https://gist.github.com/fb0ca30bc2c85c01f3626cbdfdd11b6d

Below is the sample output from running a 'FETCH_STOCK_PRICE' action through a Redux store with fetchEpic() and reducer(). The 'FETCH_STOCK_PRICE' action goes through, fetchEpic() sees this action and sends off an HTTP request. When fetchEpic() gets a response from the IEX API, it sends out a 'FETCH_STOCK_PRICE_SUCCESS' action, and then the reducer updates the state.

https://gist.github.com/f6f1c9106eedaf429dd8b320525e5931

Moving On

Redux-observable is a tool for handling async logic with React and Redux. This is important because React doesn't generally support async functions. Redux-observable is an interesting alternative to redux-saga and redux-thunk, particularly if you're already experienced with RxJS. So next time you find yourself wanting to write your own promise middleware, give redux-observable a shot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment