How to handle Async operations with Redux

It comes as no surprise to anyone that JavaScript is everywhere.
Here at Imaginary Cloud, the most part of the projects we are involved in, use it as the main language. My last project was a fairly large application that involved the usual culprits of the JavaScript ecosystem. We used a React front-end and a Node.js back-end stack.

Why do we use Redux?

Unfortunately, at that time, a clear strategy for state management wasn’t defined. As the project evolved, it started becoming an issue, so the team decided to migrate all the logic related to state management to a Class that presented itself as a Singleton. That would help with all the state-related storage and events. This solution was fine on the short-term, but eventually, it would outgrow his usefulness and a plan was set in motion to find a better alternative. It came in the form of Redux, helped by the introduction of Redux Toolkit, previously known as Redux Starter Kit.

What is a Redux Store?

Who created Redux?

Where is Redux used?

Dispatching Async Actions using Redux

How to handle Async Actions in Redux

Given the same arguments, it should calculate the next state and return it. No surprises. No side effects. No API calls. No mutations. Just a calculation.

So, where should you apply async calls in Redux?
The Actions should be the immediate answer, but the basic implementation of actions is nothing more than a plain JavaScript object you use to pass information to your store. For this reason, the community came up with several middlewares that wrap all the logic into functions instead, which mimic the natural behavior of the store.

Which Async Redux Middleware should you pick?

Why Redux Thunk?

How to implement Redux Thunk?

This will get you a fresh new app using React with all the Redux modules we needed to do this short tutorial. We will also be working with the WoofBot API service.

The first thing to do is to set up a dog slice, where you’ll keep all the information related to your dog API response.

We have our Actions:

  • uploadBreeds: Will be used as a dump of all the payload information regarding the dog breeds.
  • uploadBreedImage: Will be used to uploading certain breeds specific images, if needed.
  • loadingState: Will be used to updating the status of the request.

and Selectors:

  • selectBreeds: Returns an array of all the breeds in store.
  • selectBreedImage: Returns the image for a specific breed.
  • isLoading: Returns the status of the request

How would you normally implement this back and forth with the API and the store? I would fit it all into a useEffect hook, similar to this one:

What are we doing here?

  1. Component is mounted
  2. Loading State is set to Request with one dispatch of an action
  3. Data is requested using a simple fetch
  4. Data is received from the API and processed to the object we want
  5. Breed information in the slice is set to what we got using another dispatch
  6. Loading State is set back to Waiting

Alternatively, we might receive an error from the API which will stop the flow in step 4 and set the Loading State to Error.

This works, and that’s ok. But it also has several downsides. Mainly, it puts too much logic in the component. It’s not reusable, and if you want to access this information somewhere else, you’ll always need to make sure this component is loaded first.

Now with Thunks:

npm install --save redux-thunk

The component logic looks like this:

We need to create a new fetchBreeds action that looks very similar to the logic we previously had in the component:

This simple change of location in the code fixes most of the issues we had previously. We’ve abstracted code from the component and we’ve made this specific piece of logic re-usable throughout the entire code base. This information isn’t bound to mounting the component anymore, so now you can issue a new fetchBreeds action and the data will be loaded.

This also enables us to chain Thunks in case need more complicated logic inside our actions. We can also access the state directly, instead of needing selectors. However, you’ll still want to use selectors to make sure that any changes to Redux don’t affect your Thunks.

In the end, what to pick to handle async operations in Redux?

In the most recent issues I’ve faced, Thunks was more than enough to satisfy all my edge cases. But maybe in your case, you’ll need a more specific solution.

Do your research, keep yourself updated and you’ll always have the best tool on your side.

Found this article useful? You might like these ones too!

Originally published at https://www.imaginarycloud.com on July 16, 2020.

Applying our own Product Design Process to bring great digital products to life | www.imaginarycloud.com