Make async actions easy.
Redux-Observable is the great library based on Rxjs and helps you to handle side-effects of Redux managable application.
To reduce the boilerplate of handling async actions, I've created the redux-async-epic. It includes async-epic itself and gives you some handy helpers to manage outgoing actions.
Instalation
To install the stable version:
npm install --save redux-async-epic
This assumes you are using npm as your package manager.
Dependencies
The library depends only on rxjs but also has 2 peer dependencies:
- redux-observable
- redux
Why do I need it?
Usually async actions includes 3 major steps:
- request (turn on pending status)
- wait for response
- response (turn of pending status)
That's why you have to keep minimum three actions:
{type: "fetch"}
{type: "success"}
{type: "failure"}
if you want to abort the process you will need a fourth action
{type: "abort"}
So, each time when you working with it using redux-observable you have to make something like this:
// epic.js ; const epic = action$
A boilerplate action creators.
// actions.js const types = fetch: "fetch" success: "success" failure: "failure" abort: "abort"; const fetch = type: typesfetch payload; const success = type: typessuccess payload; const failure = type: typesfailure error; const abort = type: typesabort;
And finally the reducer:
// reducer.js ; const initialState = uiStatus: "idle" items: error: null ; { };
There is lot of boilerplate code, isn't it?
Could it be better?
It's time to redux-async-epic to shine! To use it, you need to combine async epic to your root epic.
// root-epic.js ;; const rootEpit = ; // more of your epics
Now you should define your async action as flux-standard-action:
// actions.js ; const types = fetch: "fetch"; const fetch = type: typesfetch payload meta: async: true ; const abort = type: // generates: fetch/abort;
That's it!
- You don't need to create more actions to handle response or pending status
- You don't need to create epic that handles the fetch action
And finally the reducer:
// reducer.js; // you can reduce a boilerpalte even more by using "redux-actions";; const initialState = uiStatus: "idle" items: error: null ; typesfetch: { return ...state uiStatus: "pending" ; } : { return ...state uiStatus: "success" items: actionpayload ; } : { return ...state uiStatus: "failure" error: actionpayload ; } getAbortType: { return ...state uiStatus: "idle" ; };
How it works?
When epic gets an async action it generates three more actions:
{type: "fetch"}
{type: "fetch/pending", payload: true}
{type: "fetch/success"}
or{type: "fetch/failure"}
{type: "fetch/pending", payload: false}
When async action is pending it also is listening the {type: "fetch/abort"}
action, to break the execution.
The benefits
Showing background process
As you can see, each time when async action fires it follows by 2 pending
actions. That's why you can listen for it and make a global UI spinner.
Global error handler
When async action is failed redux-async-epic generates a special failure
action which looks like that:
type: "async-action/failure" error: "something went wront" meta: failure: true originalPayload: user: "admin"
Where failure is a special unique symbol. Here the example how you can make a global error handler:
// global failure epic;; // just for example;;; $ action;
Fire another action on succes
In this example we are requiring first 10 comments, and when request is fulfilled we fire another request to get next page of comments.
// helperconst commentRequest = ; // primary actionconst fetchComments = payload = offset: 0 limit: 10 type: "fetch" payload meta: async: true method: commentRequest ; // on-success actionconst prefetchNextPage = { const config = ...payload offset: payloadoffset + payloadlimit ; return type: "prefetch" payload: config meta: async: true method: commentRequest ;};
You can fire even more actions by passing an array of them:
;