Part 32: Asynchronous Redux with createAsyncThunk

Part 32 Technology Updated September 12, 2026

Part 32: Asynchronous Redux with createAsyncThunk

Redux stores synchronous state by nature. When dealing with asynchronous operations—such as calling REST APIs—Redux Toolkit provides createAsyncThunk.


1. Defining an Async Thunk

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

// 1. Define the asynchronous thunk action
export const fetchPosts = createAsyncThunk(
  'posts/fetchPosts',
  async (category, { rejectWithValue }) => {
    try {
      const response = await fetch(`https://jsonplaceholder.typicode.com/posts?category=${category}`);
      if (!response.ok) throw new Error('Network error');
      return await response.json();
    } catch (err) {
      return rejectWithValue(err.message);
    }
  }
);

// 2. Handle pending, fulfilled, and rejected lifecycle in extraReducers
const postsSlice = createSlice({
  name: 'posts',
  initialState: {
    items: [],
    loading: false,
    error: null,
  },
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(fetchPosts.pending, (state) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(fetchPosts.fulfilled, (state, action) => {
        state.loading = false;
        state.items = action.payload;
      })
      .addCase(fetchPosts.rejected, (state, action) => {
        state.loading = false;
        state.error = action.payload || 'Failed to fetch posts';
      });
  },
});

export default postsSlice.reducer;

2. Dispatching the Thunk in React

import { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { fetchPosts } from './postsSlice';

export default function PostsFeed() {
  const dispatch = useDispatch();
  const { items, loading, error } = useSelector((state) => state.posts);

  useEffect(() => {
    dispatch(fetchPosts('tech'));
  }, [dispatch]);

  if (loading) return <p>Loading posts...</p>;
  if (error) return <p className="text-red-500">Error: {error}</p>;

  return (
    <ul>
      {items.map((post) => (
        <li key={post.id} className="p-2 border-b">{post.title}</li>
      ))}
    </ul>
  );
}

Key Takeaways

  • createAsyncThunk automatically dispatches pending, fulfilled, and rejected actions.
  • Use extraReducers with builder callback syntax to update state based on promise outcomes.

In Part 33, we explore Zustand, the ultra-lightweight state management alternative!