Make API call in React Redux Toolkit | React Redux

Chanuga Tharindu
6 min readJun 11, 2023

--

Photo by Fili Santillán on Unsplash

First of all we need to create a react app to start our proceedings. For that you can use npx create-react-app or any other mechanism.

Then we need to Install redux to the application using npm install @reduxjs/toolkit react-redux. (You can see more details on redux official page. https://redux-toolkit.js.org/tutorials/quick-start)

Once you install the dependencies you can see in the package.json like this.

For the better file structure create a folder call redux inside your src folder.

Inside that create a file call store.js. In store.js, import configureStore from ‘@reduxjs/toolkit’. What this does is using the configureStore function to create a Redux store.

import { configureStore } from '@reduxjs/toolkit';
export const store = configureStore({

});

Inside the redux folder create a folder call slice. All the slices related to the project goes inside of this folder.

Since the api we are using for this is related to todos, I am going to create a todoSlice.

Then import the createSlice to the store. createSlice is called with an object as its argument. The object contains properties defining the behavior of the slice. The name property is set to “todo”, which represents the name of the slice. It will be used to generate the action types and action creators for this slice. The createSlice function returns an object that includes the generated reducer function. The generated reducer function is exported as the default export of the module.

  import { createSlice } from '@reduxjs/toolkit';

const todoSlice = createSlice({
name: "todo",
});

export default todoSlice.reducer;

After that we should go to our index.js file. In there we should import { Provider } from ‘react-redux’. Then using provider we should wrap the app component. The Provider component is imported from the react-redux library. It is responsible for providing the Redux store to your React application. The Redux store is imported from the ./redux/store module. The store variable holds the configured Redux store. The Provider component wraps your App component, making the Redux store accessible to all components within App. The store prop is passed to the Provider component, specifying the Redux store to be used. By wrapping your App component with the Provider component and passing the Redux store through the store prop, all components within App can access the Redux store and interact with the application’s state using Redux mechanisms like connect or hooks like useSelector and useDispatch.

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { Provider } from 'react-redux'
import { store } from './redux/store'

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

This is the API endpoint we are going to use to get todo list (https://jsonplaceholder.typicode.com/todos).

To do the aAPI call first we need to import createAsyncThunk to todoSlice.js. Then we have to create the action using createAsyncThunk.

export const fetchTodos = createAsyncThunk("fetchTodos", async () => {
const res = await fetch(`https://jsonplaceholder.typicode.com/todos`);
return res?.json();
});

The createAsyncThunk function is imported from @reduxjs/toolkit. createAsyncThunk is called with two arguments. The first argument is a string “fetchTodos”, which represents the name of the thunk action. This name will be used to generate action types for the pending, fulfilled, and rejected states of the asynchronous operation. The second argument is an asynchronous function that will be executed when the thunk action is dispatched. Within the async function, an HTTP request is made to https://jsonplaceholder.typicode.com/todos using the fetch function. The response is stored in the res variable. The response is then parsed as JSON using the json() method, which returns a promise. The result of this promise will be the data fetched from the API. The async function returns the JSON data fetched from the API, which will be used as the payload of the action dispatched by the thunk. After defining the fetchTodos thunk action, you can use it with Redux’s dispatch function to initiate the asynchronous operation and handle the different states (pending, fulfilled, rejected) in your Redux reducer or with the createAsyncThunk generated action creators.

Apart from the name we are going to add initialState and extraReducers to the slice.

const todoSlice = createSlice({
name: "todo",
initialState: {
isLoading: false,
data: [],
isError: false
},
extraReducers: (builder) => {
builder.addCase(fetchTodos.pending, (state, action) => {
state.isLoading = true;
})
builder.addCase(fetchTodos.fulfilled, (state, action) => {
state.isLoading = false;
state.data = action.payload;
})
builder.addCase(fetchTodos.rejected, (state, action) => {
state.isError = true;
})
}
});

The initialState object defines the initial state of the todo slice. It has three properties: isLoading, data, and isError. The isLoading property is initially set to false, data is an empty array, and isError is set to false. The extraReducers property is a function that takes a builder object. Inside this function, you can define additional reducers for handling different actions. The builder.addCase method is used to define reducers for specific action types. In this case, it is used to handle the pending, fulfilled, and rejected states of the fetchTodos asynchronous thunk action. The fetchTodos.pending action type represents the pending state of the fetchTodos thunk. When this action is dispatched, the reducer sets the isLoading property of the state to true. The fetchTodos.fulfilled action type represents the fulfilled state of the fetchTodos thunk. When this action is dispatched, the reducer sets the isLoading property to false and updates the data property with the payload of the action (the fetched data). The fetchTodos.rejected action type represents the rejected state of the fetchTodos thunk. When this action is dispatched, the reducer sets the isError property to true. With this code, the todoSlice reducer will handle the different states of the fetchTodos asynchronous thunk action and update the corresponding properties in the state accordingly.

After that we can modify the app.js like this.

import { useDispatch, useSelector } from 'react-redux';
import { fetchTodos } from './redux/slice/todoSlice'
import './App.css';

function App() {
const dispatch = useDispatch();
const state = useSelector((state) => state);

console.log("state", state);
return (
<div className="App">
<button onClick={(e) => dispatch(fetchTodos())}>Click</button>
<br/>
{state?.todo?.isLoading && <><b>Loading...</b></>}
{state?.todo?.data?.map((i) => { return (
<li>{i.title}</li>
)})}
</div>
);
}

export default App;

The useDispatch hook is imported from react-redux to access the dispatch function. The useSelector hook is imported from react-redux to select and access the Redux state. The fetchTodos thunk action is imported from ./redux/slice/todoSlice. The App component is defined as a functional component. The dispatch function is called to get the reference to the dispatcher. The state variable is assigned the result of calling useSelector with a selector function that selects the entire Redux state. The console.log statement logs the state to the console for debugging purposes. The JSX code renders a <div> with a button inside it. When the button is clicked, the onClick event handler dispatches the fetchTodos thunk action by calling dispatch(fetchTodos()). The {state?.todo?.isLoading && <><b>Loading…</b></>} line conditionally renders the text “Loading…” if the isLoading property in the Redux state is true. The {state?.todo?.data?.map((i) => <li>{i.title}</li>)} line maps over the data array in the Redux state and renders a list item (<li>) for each item, displaying the title property of each item. With this code, the App component is connected to the Redux store. Clicking the button will dispatch the fetchTodos thunk action, which will trigger the corresponding reducers defined in the todoSlice. The component then accesses the Redux state using the useSelector hook and renders the state accordingly.

you can use Redux DevTools browswer extension to see how the states are getting chage through out the API calling process.

This is the github repository for the sample code. (https://github.com/Chanuga/react-redux-api)

Cheers,

Happy coding …

--

--

Responses (3)