Handling Errors with createAsyncThunk

Use the rejected lifecycle action of createAsyncThunk to handle API errors gracefully and update the state accordingly.
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';

// Define the initial state of our slice
const initialState = {
  books: [],
  loading: false,
  error: null

// Async thunk for fetching books
export const fetchBooks = createAsyncThunk(
  async (arg, { rejectWithValue }) => {
    try {
      const response = await apiService.fetchBooks(arg);
      // Assume apiService is a service that makes the actual API call
    } catch (error) {
      // Assume the error object has a response field with the error details
      return rejectWithValue(;

// Slice for handling book-related state
export const booksSlice = createSlice({
  name: 'books',
  reducers: {},
  extraReducers: (builder) => {
    builder.addCase(fetchBooks.pending, (state) => {
      state.loading = true;
    .addCase(fetchBooks.fulfilled, (state, action) => {
      state.loading = false;
      state.books = action.payload;
      state.error = null;
    .addCase(fetchBooks.rejected, (state, action) => {
      state.loading = false;
      state.error = action.payload || 'Failed to fetch books';
      state.books = [];

export default booksSlice.reducer;
This code defines a booksSlice using Redux Toolkit's createSlice, where it sets up an initial state for books, loading, and error. It also defines a fetchBooks async thunk to fetch books from an API. If the fetch is successful, the state is updated with the fetched books. If it fails, rejectWithValue is called with the error details, which updates the state to reflect the error, and the books list is set to an empty array.