I’ve got 99 problems, but asynchronous side-effects in React ain't one

Steffen Lorang Ekeberg
Sopra Steria Norge
Published in
4 min readMay 6, 2020

If you’re having side effects trouble I feel bad for you, son. Let's look at how redux-saga can make your life easier.

This article requires a basic understanding of React and Redux. If you want a quick refresh, I suggest checking out the official react tutorial before moving on to this introduction to redux.

So, what is redux-saga anyway?

redux-saga is 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, easy to test, and better at handling failures.

This sounds great. Let’s take it for a spin and see if redux-saga can help us deal with some asynchronous side effects.

First, let’s create a React app and install the necessary dependencies. The npm package create-react-app is a great tool for easily creating a react web app with one command. Let’s install it and generate a demo project and fire it up.

npm install -g create-react-app
create-react-app redux-demo
cd redux-demo
npm start

You should now be able to view the default app at http://localhost:3000. The page will automatically reload if you make changes to the code. Now we are ready to install redux, redux-saga, and axios.

npm i --save redux redux-saga axios

Neat. Let’s get started with setting up redux. The first thing we need is a redux store.

src/redux/store.js

Now we need to wrap our application with a store provider.

src/App.js

Then we are ready to make it all come alive in our DemoComponent.

src/components/DemoComponent.js

So, what's going on here? We have connected our DemoComponent to the redux state with the useSelector hook. When we push the button we should see the name, John Doe.

In real life, things obviously aren’t this simple, and some actions require asynchronous side-effects, and often multiple side effects that have to run in a specific order, where there are several points of failure. This is where redux-saga comes in.

For the remainder of this article, we are going to build upon our existing Redux structure, but let us imagine the following things needs to happen when we trigger the SET_NAME action

  1. An async call to an API asking for the first name
  2. An async call to a separate API asking for the last name
  3. Updating the store and displaying the value to the user.

Since we now have async side effects, let’s solve these by using redux-saga.

Let’s start by modifying our Redux store to include the saga middleware as part of the store setup.

Updated src/store.js

As you can see the store now is initialized with the redux-saga middleware hooked up. Let's have a look at our sagas:

src/sagas/sagas.js

As you can see from the function* annotation, as well as the yield keyword, redux-saga is based on generator functions. You should be able to grasp redux-saga even though you don’t have a full understanding of the underlying workings of generator functions, but if you are curious you can check out this article for an introduction.

So, what's going on?

  • We have included the saga middleware as part of our Redux setup, which enables the sagas to listen and react to actions dispatched.
  • Our DemoComponent dispatches a type of “SET_NAME”
  • The watchSetNameSaga takes every action of type “SET_NAME” and triggers the setNameSaga. In the setName saga, we fetch the first name and the last name from an API and dispatch the “SET_NAME_FULFILLED” action with the full name after both API calls have completed.
  • The reducer updates the state on the “SET_NAME_FULFILLED” action, and our DemoComponent is re-rendered with the updated state.

The action flow has now changed from Action Reducer,
to Action → Saga → Reducer.

Notice the takeEvery effect which we used in our watchSetNameSaga. This is a redux-saga effect that ensures that all actions dispatched of that type trigger the setNameSaga. This is one of many effects you have at your disposal when using the redux-saga middleware, a quick introduction to some of them:

  • takeLatest: takes only the latest action of a given type, and cancels all previously started sagas. No more unnecessary spamming of your API.
  • race: set a race between two effects, great for when you have a cancelable action by the user.
  • retry: well, its sort of obvious. It retries the given function for a set number of times.
  • And many many more, check of the API reference for a full list

Another great feature of redux-saga is the testable nature of generator functions. Let's write a quick test for our setNameSaga using jest and redux-saga-testable just to demonstrate how trivial this is:

usrc/sagas/sagas.test.js

As you can see, redux-saga helps you with a set of commonly faced problems when dealing with asynchronous side effects. The goal of the introduction was for you to be able to see the most significant advantages of redux-saga so that you might better understand the scenarios in which redux-saga might be the right tool for the job.

--

--