DEV Community

ZeeshanAli-0704
ZeeshanAli-0704

Posted on

Simplest possible Redux Thunk example

WHAT is Redux Thunk?

Redux Thunk is a middleware that allows you to write functions (not just plain objects) as actions.

Normally Redux ONLY accepts:

{ type: "INCREMENT" }   // plain object
Enter fullscreen mode Exit fullscreen mode

But with Thunk, you can dispatch functions like:

(dispatch) => {
   // async code
}
Enter fullscreen mode Exit fullscreen mode

πŸŸ₯ Without Thunk (what happens?)

If you try:

dispatch(fetchUsers());

Redux expects fetchUsers() to return an object like:

{ type: "FETCH_USERS" }

But Thunk returns a function:

(dispatch) => { ... }

Redux will say: ❌ β€œI don’t understand functions.”

Redux Thunk tells Redux:
πŸ‘‰ β€œIf the action is a function, call it and pass dispatch as argument.”

This is how async API calls become possible in Redux.

βœ… 1. Install Redux Toolkit + Thunk

(Thunk comes built-in with Redux Toolkit)

npm install @reduxjs/toolkit react-redux
Enter fullscreen mode Exit fullscreen mode

βœ… 2. Create a Slice + Thunk

features/users/userSlice.js

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

// ----- Thunk (async action creator) -----
export const fetchUsers = () => {
  return async (dispatch) => {
    dispatch(fetchStart());

    try {
      const res = await fetch("https://jsonplaceholder.typicode.com/users");
      const data = await res.json();
      dispatch(fetchSuccess(data));
    } catch (err) {
      dispatch(fetchFail(err.message));
    }
  };
};

const userSlice = createSlice({
  name: "users",
  initialState: {
    loading: false,
    data: [],
    error: null,
  },
  reducers: {
    fetchStart(state) {
      state.loading = true;
      state.error = null;
    },
    fetchSuccess(state, action) {
      state.loading = false;
      state.data = action.payload;
    },
    fetchFail(state, action) {
      state.loading = false;
      state.error = action.payload;
    },
  },
});

export const { fetchStart, fetchSuccess, fetchFail } = userSlice.actions;
export default userSlice.reducer;
Enter fullscreen mode Exit fullscreen mode

βœ… 3. Add reducer to the store

store.js

import { configureStore } from "@reduxjs/toolkit";
import userReducer from "./features/users/userSlice";

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

export default store;
Enter fullscreen mode Exit fullscreen mode

βœ… 4. Use Thunk in a Component

App.js

import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { fetchUsers } from "./features/users/userSlice";

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

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

  return (
    <div>
      <h2>Users</h2>

      {loading && <p>Loading...</p>}
      {error && <p>Error: {error}</p>}

      {data.map((u) => (
        <p key={u.id}>{u.name}</p>
      ))}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

πŸŽ‰ That's it!

You now have:

  • Redux slice
  • Async thunk action creator
  • Working API call
  • Component consuming Redux state

If you want, I can also show:

βœ… same example using createAsyncThunk (shorter)
βœ… error handling + retries
βœ… RTK Query way (even simpler)

Just tell me!

Top comments (0)