DEV Community

Prakash Bhatt
Prakash Bhatt

Posted on

React + Redux Toolkit + Axios

πŸš€ A Modern React + Redux Toolkit + Axios Setup (Step-by-Step Guide)

Working with APIs in React becomes much easier when you combine Redux Toolkit + Axios with a clean, scalable folder structure.

This guide shows a production-style architecture that you can use in any real-world React project.

πŸ“‚ Folder Structure
src/
β”‚
β”œβ”€β”€ api/
β”‚ β”œβ”€β”€ api.js # axios instance (reusable)
β”‚ └── endpoints.js # all API functions
β”‚
β”œβ”€β”€ app/
β”‚ └── store.js # Redux store setup
β”‚
β”œβ”€β”€ features/
β”‚ └── userSlice.js # Slice + async thunks
β”‚
β”œβ”€β”€ App.jsx
└── main.jsx

This structure keeps your API logic clean and helps scale your app easily.

*πŸ› οΈ 1. Reusable Axios Instance (api.js)
*

import axios from "axios";

const api = axios.create({
baseURL: "http://localhost:5000/api",
});

// Attach token automatically
api.interceptors.request.use((config) => {
const token = localStorage.getItem("token");
if (token) {
config.headers.Authorization = Bearer ${token};
}
return config;
});

// Global error handler
api.interceptors.response.use(
(res) => res,
(err) => {
console.error("API Error:", err.response?.data || err.message);
return Promise.reject(err);
}
);

export default api;

The benefits:

One place to manage API configuration

Auto token support

Clean, reusable API calls
**
πŸ“‘ 2. API Endpoints (endpoints.js)**
import api from "./api";

// GET /users
export const fetchAllUsers = () => api.get("/users");

// Examples:
// export const loginUser = (data) => api.post("/auth/login", data);
// export const createUser = (data) => api.post("/users", data);

This file keeps all API methods clean and organized.

βš™οΈ 3. Redux Slice + AsyncThunk (userSlice.js)
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import { fetchAllUsers } from "../api/endpoints";

export const getUsers = createAsyncThunk("user/getUsers", async () => {
const res = await fetchAllUsers();
return res.data;
});

const userSlice = createSlice({
name: "user",
initialState: {
users: [],
loading: false,
error: null,
},
extraReducers: (builder) => {
builder
.addCase(getUsers.pending, (state) => {
state.loading = true;
state.error = null;
})
.addCase(getUsers.fulfilled, (state, action) => {
state.loading = false;
state.users = action.payload;
})
.addCase(getUsers.rejected, (state, action) => {
state.loading = false;
state.error = action.error.message;
});
},
});

export default userSlice.reducer;

This handles:

Loading state

Error state

Updating Redux store with API data

πŸ—„οΈ 4. Configure the Store (store.js)
import { configureStore } from "@reduxjs/toolkit";
import userReducer from "../features/userSlice";

export const store = configureStore({
reducer: {
user: userReducer,
},
});

πŸ”Œ 5. Provider Setup (main.jsx)
import React from "react";
import ReactDOM from "react-dom/client";
import { Provider } from "react-redux";
import { store } from "./app/store";
import App from "./App";

ReactDOM.createRoot(document.getElementById("root")).render(



);

🎨** 6. Using the API Data in React (App.jsx)**
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { getUsers } from "./features/userSlice";

function App() {
const dispatch = useDispatch();
const { users, loading, error } = useSelector((state) => state.user);

useEffect(() => {
dispatch(getUsers());
}, [dispatch]);

if (loading) return

Loading...

;
if (error) return

Error: {error}

;

return (


Users List


{users.map((u) => (

{u.name}


))}

);
}

export default App;

🎯 Final Thoughts

With this setup you get:
πŸ”₯ Clean architecture
πŸ”Reusable API code
⚑ Fast development with Redux Toolkit
🧩 Scalable project structure

Top comments (0)