Handle side-effects with Redux-Saga

[av_textblock size=” av-medium-font-size=” av-small-font-size=” av-mini-font-size=” font_color=” color=” id=” custom_class=” av_uid=’av-ka7vlout’ admin_preview_bg=”]

Handle side-effects with Redux-Saga

[/av_textblock]

[av_hr class=’invisible’ icon_select=’yes’ icon=’ue808′ font=’entypo-fontello’ position=’center’ shadow=’no-shadow’ height=’50’ custom_border=’av-border-thin’ custom_width=’50px’ custom_margin_top=’30px’ custom_margin_bottom=’30px’ custom_border_color=” custom_icon_color=” id=” custom_class=” av_uid=’av-ka7vdtw6′ admin_preview_bg=”]

[av_textblock size=” av-medium-font-size=” av-small-font-size=” av-mini-font-size=” font_color=” color=” id=” custom_class=” av_uid=’av-ka7vd6in’ admin_preview_bg=”]

Introduction

One of hardest parts in developing and maintaining React/Redux application is handling asynchronous actions: timeouts, requests/response handling, interaction with third-party stores, different callbacks and so on without сomplicating logic into actions-creators and reducers.

In this post, I’m going a real project to show you how to move all side-effects ( and not only ) away from action-creators. All of this in a way that makes them as clean as possible with Redux-Saga.
[/av_textblock]

[av_hr class=’invisible’ icon_select=’yes’ icon=’ue808′ font=’entypo-fontello’ position=’center’ shadow=’no-shadow’ height=’50’ custom_border=’av-border-thin’ custom_width=’50px’ custom_margin_top=’30px’ custom_margin_bottom=’30px’ custom_border_color=” custom_icon_color=” id=” custom_class=” av_uid=’av-ka7vdtw6′ admin_preview_bg=”]

[av_video src=’https://www.youtube.com/watch?v=EcGZdzECmIk&utm_source=video&utm_medium=Handle%20side-effects%20with%20Redux-Saga&utm_campaign=YouTube’ mobile_image=” attachment=” attachment_size=” format=’16-9′ width=’16’ height=’9′ conditional_play=” id=” custom_class=” av_uid=’av-ka7vc9r5′]

[av_textblock size=” av-medium-font-size=” av-small-font-size=” av-mini-font-size=” font_color=” color=” id=” custom_class=” av_uid=’av-ka7vd1ab’ admin_preview_bg=”]

What is redux-Saga?

Redux-Saga is a library that aims to make application side effects easier to manage, more efficient to execute and simpler to test.

If you were working on some React/Redux projects, you were probably using Redux-Thunk for asynchronous work.

Redux-Thunk is the standard way to perform asynchronous operations on Redux. It also introduces a concept of thunk – a function that wraps an expression to delay its evaluation.
It is nice and will probably do its job well on the major part of the projects. But as your project grows it becomes difficult to manage and to develop new features.

It’s a widespread problem when developers end in callback hell in the large-size project with a large amount of interconnected asynchronous actions. And this is an exact case where Redux-Saga rocks.
Redux-Saga aims to make side-effects management simpler and better using saga.

Redux-Saga is known as a middleware layer in Redux ecosystem. It coordinates and induces asynchronous actions (side-effects).
With Middleware Redux flow diagram looks like this:

redux middleware diagram

To make asynchronous flow easier, Redux-Saga uses ES6 Generators

Generators are functions that can be stopped and continued, instead of executing all expressions in a single step.

When you call a generator function, it returns an iterator object. With each call to the iterator’s next() method, the body of the generator function executes until the next yield expression and then stops.
Example of ES Generators:

Let’s look on an example with asynchronous code:

The real project example

As I wrote before, I would like to show you how we refactored action-creators with the saga and made them cleaner, more readable and easy for testing.
So, here is a part of reducer and action creators responsible for authorization realized with Redux-Thunk:

As you can see, we have 4 actions here:

AUTH_START – set loading property for showing spinner on UI
AUTH_SUCCESS – set the token property with userId when a user is successfully authorized
AUTH_FAIL – set error property if some error occurs during authorization
AUTH_LOGOUT – clear user data from state

The things are getting more complicated here. So let me describe the logic:

– authStartauthSuccessauthFaillogout – are simple and don’t need to be explained.
– auth – sends a request to a server and handle its response. If the response is successful, stores response data to localStorage for future use, dispatches AUTH_SUCCESS action and calls checkAuthTimeout method.
– checkAuthTimeout – waits until tokens expiration time and dispatches AUTH_LOGOUT action.

As you can see, we have some asynchronous code here. As your project grows, more and more logic is applied.

So lets see how do following action-creators look like when you move all additional logic to sagas:

As you could notice, all action-creators became pure functions with a single responsibility to return an action. But I also added a few new actions: AUTH_USERAUTH_INITIATE_LOGOUTAUTH_CHECK_TIMEOUT.
I added them to separate the actions that should get to Redux-Saga from those that need to get to reducers.

For better understanding let look on sagas example:

In this example, the sagas can be divided into 2 types:
1. saga-watcherwatchAuthSaga
2. saga-workerauthUsercheckAuthTimeoutlogout

These terms refer to a way of organizing the control flow in Redux-Saga.
– saga-watcher is watching for dispatched actions and spawns a new task on every action. You can think about it as another implicit layer in Redux ecosystem.

It combines async actions (saga-worker) into the broader business logic and gives you more flexibility to implement complex solutions.
– saga-worker is responsible for handling all logic required by action and terminating them.

saga-watcher workflow in our example is quite simple. When it triggers an action that should be dispatched, it spawns a new saga-worker instance using the takeEvery API.
If there are multiple requests, takeEvery creates multiple instances of the saga-worker but it does not guarantee that all tasks will terminate in the same order they were started.

Instead of takeEvery we can use another Redux-Saga API takeLatest.

As you can guess, takeLatest , unlike the takeEvery API, does not create multiple saga-worker instances for multiple dispatched actions of the same type.
It spawns a task on each dispatched action that matches the pattern but automatically cancels any task running previously.

Let’s take a closer look on saga-workers workflow:

Let’s begin with authUser:

  1. Dispatches the action to show the spinner.
  2. Executes an API call.
  3. Waits for response and if API call succeeds:
    • Calculates token expiration date
    • Stores user-specific data to the localStorage
    • Dispatches AUTH_SUCCESS and AUTH_CHECK_TIMEOUT actions
  4. In case of failure, dispatches AUTH_FAIL action

In this saga-worker we used two new Redux-Saga API: putand call.

The call method returns the object describing operation we want to execute.
– Redux-Saga is able to take care of the call and returns the results to the generator function. – Similar logic is followed by the put method. Instead of dispatching action inside a generator function, put returns an object with instructions for our middleware.

These returning objects are also called Effects. Similar to NgRx/Effects. As you could noticed, unlike call method, put doesn’t block a flow.

In Redux-Saga there are blocking and non-blocking calls.

A blocking call means that the saga yielded an Effect and will wait for the result of its execution before resuming to the next instruction inside the yielding Generator.
A non-blocking call means that the saga will resume immediately after yielding the Effect.

Let’s also look at checkAuthTimeout workflow:
1. Waits until the token expires
2. Dispatches AUTH_LOGOUT action

In this example, I’ve used another blocking call API delay.
As you can guess from the example this method is very similar to native setTimeout.
It allows us to do the same things as setTimeout but writing less code.

In addition to methods shown in this example, Redux-Saga offers many other effects-creators.

How to bind Redux-Saga to a project

Redux-Saga should be registered in your project to be used.

Summary

There are no standard solutions to work with side-effects in Redux. Redux-Saga will not improve the performance of your program. But it will make the code more organized and readable.
Therefore, it’s up to you to choose which library to use and how to deal with side-effects in your project.

Useful links

Do you like this post? Want to stay updated? Follow us on Twitter or subscribe to our Feed.
[/av_textblock]

Author

Oleksandr Oleksiv

I'm a Frontend developer with experience in web development. I love to learn, discover and experiment with new technologies and approaches. I am experienced in managing a team of developers, web development and backend development (Node.JS). Such a wide range of experiences helped me to gain knowledge about all the intricacies of the interaction between the client and the server. Oleksandr is the guy you want to stick around - a very friendly, purposeful and persistent person. Always ready to share his knowledge with teammates and learn from other developers. You can find him on GitHub as well as LinkedIn

Latest Blogposts

13.11.2020 / By Anna Berkolec

How to achieve your goals by creating good habits instead of setting milestones

Did you know that about 40% of what we do every day we do automatically? Almost half of our life is taken up by habits that we've formed over our lifetime. People are simple creatures, and if something works, why not stick with it?

28.10.2020 / By Maciej Greń

Tubi x Scala: Why it works?

If you want to succeed in a World of ever-changing technology, you always need to be a step ahead of your competitors. Constantly pushing the boundaries is crucial if your business is to provide a flawless customer experience, and grow. Today, we’re going to show you the perfect example of a company that wasn’t scared of scaling up machine experimentation with Scala. And let us just take a sneak peek at that in one sentence: “I love working with Scala” | Alexandros Bantis, backend Scala Developer at Tubi The outcome is rather impressive, so read on to find out more about it!

01.10.2020 / By Daria Karasek

Do Scala with Scalac – 7 success stories to follow

From challenges to achieving goals - building a complex solution takes time and effort in order to seize all opportunities and deliver a high-quality product. Thinking about all the aspects you have to handle when developing software or making changes to existing ones can be a little overwhelming. Especially when a solution that used to work doesn’t fit your needs anymore and bottlenecks give you sleepless nights. Picking the right partner to help you manage this is a hard nut to crack. Maybe it’s high time to ask other companies about their own experience and recommendations?

Need a successful project?

Estimate project