The path to becoming even a little bit comfortable with it was winding and suboptimal, so I thought I’d write the tutorial I wish I’d had when just starting out with sagas.
This tutorial assumes you have a solid grasp of React or React Native and Redux. If you’re not ready for
redux-saga(yet), I’d definitely recommend that you check out this amazing, MASSIVE treasure trove of resources for learning React and Redux.
From the official repo:
redux-sagais a library that aims to make application side effects (i.e. asynchronous things like data fetching and impure things like accessing the browser cache) easier to manage, more efficient to execute, simple to test, and better at handling failures.
I think of it as an organized way of helping your Redux app communicate and stay in-sync with the outside world — mainly external APIs.
Many wonderful people have opined on the pros, cons, and everything else about
redux-saga, much better than I could — so here are some links if you want to really nail down the WHATs and WHYs.
We’ll be focusing on the HOW.
Create a new app with
npx create-react-app dog-saga
Confused by the
npx? So was I, until I read this.
Enter the project’s directory and fire up the app.
You should now see the boilerplate for
create-react-app, with it’s spinning React logo. You’ll be replacing it with cute dogs soon enough.
npm install --save redux
Create a new file in your
src folder called
redux.js and add the following code to it.
API_CALL_REQUESTsays that we’re beginning the process of
fetchinga dog from the Dog API.
API_CALL_SUCCESStells the Store that we successfully retrieved a
dogand are therefore no longer in the process of
API_CALL_FAILUREtells the Store that something went wrong with our API call. We received an
errorrather than a new
But how should we make the
How does the Store know whether the API call was a success or a failure?
HOW DO WE GET PICTURES OF CUTE DOGS DAMNIT??? With a saga.
P.S. We’re not gonna use action creators in this app. For something so simple, they may muddy the waters. Also, you’ll see how redux-saga handles and dispatches actions more clearly (in my opinion) without them.
We want to create a saga, using
redux-saga, that will initiate an API call for a dog image, then tell the Store whether that API call was a success or a failure.
API_CALL_SUCCESSalong with the
API_CALL_FAILUREalong with the
npm install --save redux-saga
npm install axios
Create a new file called
sagas.js and add the following code to it.
Before we walk through this new file, notice the
function* syntax. This creates a special kind of function new to ES6 called a
Generators can pause and restart — be exited and re-entered — and actually remember the context/state of the function over time.
redux-saga relies on generators, but does a decent amount of the work for us, so (in my fairly limited experience) a deep understanding of them for this use-case isn’t necessary.
Now let’s walk through
watcherSagais a saga that watches for an action to be dispatched to the Store, triggering a
takeLatestis a helper function provided by
redux-sagathat will trigger a new
workerSagawhen it sees an
API_CALL_REQUEST, while cancelling any previously triggered
workerSagastill in process.
axiosto request a random dog image from the Dog API and returns a
Promisefor the response.
fetchDog, using another
call, and stores the result (a resolved or failed
Promise) in a
fetchDogwas a success, we extract the
dogimage from the
responseand dispatch an
dogin the payload to the Store, using ANOTHER
fetchDog, we let the Store know about it by dispatching an
API_CALL_FAILUREaction with the
Phew! It’s a little weird at the beginning, but this pattern/procedure and its benefits become more clear after a few implementations.
Ok, we have our pieces, now it’s time to put them all together.
npm install --save react-redux
Also, install Redux Devtools — must-have for debugging and seeing Redux (and sagas’ related actions) in action. Go here for info on setting up the Redux dev tools. I recommend using the browser extension.
index.js file and make it look like the file below.
The Redux stuff should look familiar.
<App/>component in a
<Provider/>component with the
store, which let’s us work with Redux in React.
To make our
redux-saga work with Redux…
createSagaMiddleware, and apply it to the Redux
storewith some help from
watcherSaga, so that it can trigger the
workerSagawhen there’s an
App.js and paste the following code into it.
mapStateToPropsto make the most current state of
mapDispatchToProps, we create a function called
onRequestDogthat dispatches an
API_CALL_REQUESTaction to the Store.
Appcomponent and export this “reduxed” version of it for use in
Now let’s walk through some of the changes (top to bottom) made to the
App component’s rendered output, which allow the user to see the current state of the app and request dog images.
All of these snippets are from the
App.js above, so no new code here.
In the snippet below, we tweaked the image
src to show a
dog image if one exists in the Store. If
null, it falls back to the React
If the current
state of our app has a
dog image, we tell the user to keep clicking. If not, we tell them to replace the React
logo with a
If the current
state has an error, we display some text to let the user know.
Here, if our sagas are currently in the process of
fetching a new dog image, which means
workerSaga has not dispatched an
API_CALL_FAILURE yet, we disable the button.
Otherwise, we provide a button for the user to click and request a random dog image.
To see the
workerSaga dispatch an
API_CALL_FAILURE, go into
sagas.js and mess up the
url for the dog api (like changing “breeds” to “beds”).
Now when you click the “Request a Dog” button, the error message displays!
actionis dispatched, likely through a function declared in
actionand triggers a
workerSaga. Use saga helpers to watch for actions differently.
actionalso hits a
reducerand updates some piece of
stateto indicate that the saga has begun and is in process (e.g.
workerSagaperforms some side-effect operation (e.g.
workerSaga‘s operation, it dispatches an
actionto indicate that result. If successful (
API_CALL_SUCCESS), you might include a payload in the action (e.g.
dog). If an error (
API_CALL_FAILURE), you might send along an
errorobject for more details on what went wrong.
reducerhandles the success or failure
workerSagaand updates the Store accordingly with any new data, as well as sets the “in process” indicator (e.g.
fetching) to false.
Throughout this process, you can use the updates to Redux
state flowing through to
props to keep your user informed of the process and progress thereof.