Async Tasks and Redux-Thunk
Overview
Redux actions are submitted asynchronously. This is a significant problem for non-trivial applications that need to interface with application programming interfaces or APIs to run external effects in parallel. Redux can be thought of as middleware that handles distribution and reaches between actions and reducers. Redux actions are dispatched asynchronously, so two middleware libraries serve this purpose. Redux Saga and Redux Thunk.
What is Redux-Thunk?
Redux provides an easy way to update the application state in response to synchronous actions. However, it lacks for handling asynchronous code. This is where thunks come into play.
Redux Thunk middleware allows you to create action builders that return functions instead of actions. Thunks can be used to display sending an action, or to send it only when certain conditions are met. The inner function receives the store method dispatch and getState as parameters.
Redux thunks are used to manage the state of React applications.
Redux-thunk is not a replacement for Redux, but it extends the functionality of Redux and acts as a middleware that allows you to keep your reducer code clean from API calls, and once you have the API response, you can dispatch events without issue. Redux Thunk works when you have an API call or an asynchronous task running.
Redux Thunk is a tool in the State Management Library category of a tech stack.
Why Do I Need This?
A Plain simple Redux store can only do simple synchronous updates by performing actions. Middleware extends the functionality of the store and allows you to write asynchronous logic that interacts with the store.
Thunk is the recommended middleware for basic Redux side effect logic, including complex synchronous logic that requires access to memory, and simple asynchronous logic such as AJAX requests.
Asynchronous Calls In Redux Without Middlewares
I am trying to explain how to implement asynchronous action calls in Redux without using middleware.
Let's start by creating a simple react project using "create-react-app"
Also, in addition to Redux, we use React-Redux to make life a little easier.
To not overcomplicate things, we will only implement two API calls.
Create a new file name Api.js. This is the file that stores polling calls to endpoints.
There are three basic actions associated with each API call. That is, REQUEST, SUCCESS, and FAIL. Each API is always in one of these three states. Depending on these states, you can decide how to display your UI. You can display a loader in your UI when in the REQUEST state and a custom UI when in the FAIL state to let the user know something went wrong.
So for each API call, we're going to make, we'll create three constants: REQUEST, SUCCESS, and FAIL. In this case, the Constant.js file would look like this:
Here is the Store.js file initialState of the application:
As you can see from the code above, all API data resides in one object, the state object. Keys isLoading indicates whether the API is in the REQUEST state.
Now that we've defined our store, let's see how we can manipulate the state at different stages where there are API calls. Below is the Reducer.js file.
By giving each API call its own variable to indicate the loading phase, it's now easier to implement things like multiple loaders on the same screen, depending on which API call is in which phase. rice field.
To actually implement asynchronous behavior in action, all you need is a regular JavaScript function passing dispatch as the first argument. The function forwards the action to the store, so we pass shipping to the function. Normally the component has access to the shipping, but I want the external function to control the shipping, so I need it.
And a function to enable sending in the range of function above:
So the complete action.js file looks like this:
Once that's done, all that's left to do is pass these functions to the connected component's mapDispatchToProps.
App.js
This is how Redux makes asynchronous calls without middleware.
Async Function Middleware
A basic rule when creating reducers in React + Redux applications is that they should not have side effects. A side effect is something that can change something outside the scope of the function. Making an HTTP call is an example of a side effect.
HTTP calls are side effects, but they are an important part of the web applications. You can't make HTTP calls with reducers, but you can write middleware for that. To handle a situation like this, you'll need to create middleware for your asynchronous functions.
You can write middleware for asynchronous functions, React already provides that. Redux Thunk is just the middleware of asynchronous functions.
Installation
To use redux-thunk, you need to install it using Node Package Manager(NPM).
Once installed, you need to import the thunkMiddleware from redux-thunk into your store file. Note that the store must be able to pass thunk middleware to the dispatch function. For this, we need to use applyMiddleware
This is how we can apply "thunkMiddleware" to the store. Now let's see how we can write a thunk function.
getData is a thunk middleware function. It has two parameters, Dispatch, and GetState. The last line of the function sends the desired action on the dispatch.
So what happened here? The middleware function created above handles asynchronous HTTP calls that provide data.
Where would I write this logic if there is no redux-thunk? You can't write it directly in reducers or actions because it has no side effects. That's why we have Redux-thunk middleware that allows us to write asynchronous logic.
Remember how earlier we gave the store the ability to pass a middleware function to the shipping function? Notice the following line of code:
The getData function is called when the above code is executed anywhere in the application. No action is taken at this point. So it's perfectly fine to make an HTTP call in the getData function. When a call is made and a response is received, the "dispatch" functionality provided by redux-thunk can perform the necessary actions. From here you can continue to update your state with the normal Redux flow.
Redux-thunk is, therefore, useful in reactive applications that use Redux to manage state. Thunk middleware is neither an action nor a reducer, so it can have side effects. Additionally, it provides a Dispatch and GetState function that can be used to send actions and the access status, respectively.
First, use your terminal to navigate to your project directory and install the redux-thunk package into your project.
Now apply middleware when creating an app store using Redux's applyMiddleware. For a React application using Redux and React-Redux, the index.js file looks like this:
index.js
Redux Thunk is imported and applied to your application.
Composition
The return value of the inner function can be used as the return value of dispatch itself. This is useful for coordinating the asynchronous flow of control using thunk action builders that return promises that send to each other and wait for the complete.
Using Redux Thunk in a Sample Application
The most common use case for Redux Thunk is asynchronous communication with external APIs to retrieve or store data. Redux Thunk makes it easy to send actions to an external API that follows the request lifecycle.
Normally, to create a new todo item, you first send an action to indicate that the creation of the todo item has started. If the todo item is successfully created and returned from the external server, the new todo item triggers another action. If an error occurs and the task cannot be saved to the server, you can take action on the error instead.
Let's see how we can achieve this using Redux Thunk.
Import the action into a container component and send it.
AddTodo.js
The action uses Axios to send a Post request to the JSONPlaceholder's endpoint.
index.js
Notice that the action builder addTodo returns a function instead of a regular action object. This function gets the shipping method from the store.
Within the body of the function, we first send an immediate sync action to the store to indicate that we have started saving the task using the external API. Then use AXIOS to make the actual POST request to the server. A successful response from the server uses the data obtained from the response to send a successful synchronous action, while an error response uses the error message to send another synchronous action.
Using an external API like JSONPlaceholder in this case will allow you to see the actual network latency. However, when using a local backend server, network responses may be too fast to experience the network delays that real users experience, so you can add artificial delays during development.
index.js
To test error scenarios, you manually throw an error.
index.js
For the sake of completeness, here's an example todo reducer that handles the entire request lifecycle:
todoReducer.js
Redux-Thunk vs Redux-Saga
Redux Thunk | Redux Saga |
---|---|
Action Builder may contain too many asynchronous logic functions | Action builders remains pure functions |
Contains less boilerplate codes | Contains more boilerplate codes than Redux-Thunk |
Difficult to scale up codes | Easy to scale codes as compared to redux-thunk codes |
Difficult to taste async functions | Easy to test as all logic remains together |
As compared to redux-saga, easy to understand logic, functions, concepts | Due to multiple concepts to learn like generator functions and redux-saga, etc. It is difficult to understand |
Conclusion
- This is very useful concept that helps you deal with side effects efficiently.
- Redux allows React applications to submit actions synchronously and rely on external APIs for tight call integration of data while creating and consuming stores.
- Finally, thunks are an effective solution for applications with simple asynchronous requirements.
- Redux Thunk is middleware that allows you to call action builders that return functions instead of action objects.
- Redux Thunk uses a pattern that facilitates the abstraction of storage logic from component to services, action builders, and actions. Component doesn't care what happens to the data store. You just need to send the logic service.
- Writing middleware for Redux is a powerful tool. Redux is one of the most commonly used middleware for asynchronous actions. Thunk is also the standard asynchronous middleware for Redux Toolkit and RTK Query.