React Redux Thunk: infinite loop on dispatch

  Kiến thức lập trình

I’m trying to create loading effect when fetching data using react redux toolkits. But whenever i render it leads to infinity dispatch. How can I resolve it.

component.tsx

import { useAppDispatch, useAppSelector } from "@/app/hooks";
import { Button } from "@/components/ui/button";
import { getCourseById } from "@/features/course/courseSlice";
import { useEffect } from "react";
import { useParams } from "react-router-dom";
import { format } from "date-fns";
import FileViewer from "@/components/custom/chonky";
import { Calendar, Locate } from "lucide-react";
import { useLocation } from "react-router-dom";

export default function CoursePage() {
  const { id } = useParams<{ id: string }>();
  const dispatch = useAppDispatch();
  const singleCourse = useAppSelector((state) => state.courses?.course);
  const isSuccess = useAppSelector((state) => state.courses?.isSuccess);
  const isLoading = useAppSelector((state) => state.courses?.isLoading);
  let location = useLocation();

  useEffect(() => {
    if (id) {
      fetchCourse({
        courseId: id,
        prefix: "",
      });
    }
  }, [dispatch, location.pathname]);

  const fetchCourse = async ({
    courseId,
    prefix,
  }: {
    courseId: string;
    prefix: string | "";
  }) => {
    dispatch(
      getCourseById({
        courseId: courseId,
        prefix: prefix,
      })
    ).unwrap();
  };

  if (isLoading) {
    return <p>Loading...</p>;
  }

  return (
    <>
      {isSuccess &&
        singleCourse.hasOwnProperty("startDate") &&
        singleCourse.hasOwnProperty("endDate") && (
          <>
            <div className="mt-10 grid grid-cols-1 md:grid-cols-2 gap-8 max-w-6xl mx-auto py-6 px-4 md:px-6 border border-gray-200">
              <div className="rounded-lg overflow-hidden">
                <img
                  alt="Course Thumbnail"
                  className="w-full h-full object-cover aspect-video"
                  src={singleCourse.thumbnail}
                />
              </div>
              <div className="space-y-6">
                <div className="space-y-2">
                  <h1 className="text-3xl font-bold">{singleCourse.title}</h1>
                  <div className="flex items-center space-x-4">
                    <div className="flex space-x-2">
                      <Calendar className="w-7 h-7 text-gray-500 dark:text-gray-400" />
                      <p className="text-gray-500 dark:text-gray-400">
                        {format(
                          new Date(singleCourse?.startDate),
                          "EEEE, HH:mm dd-MM-yyyy"
                        )}{" "}
                        -{" "}
                        {format(
                          new Date(singleCourse?.endDate),
                          "EEEE, HH:mm dd-MM-yyyy"
                        )}
                      </p>
                    </div>
                    <div className="flex space-x-2">
                      <Locate className="w-7 h-7 text-gray-500 dark:text-gray-400" />
                      <p className="text-gray-500 dark:text-gray-400">
                        Training 1
                      </p>
                    </div>
                  </div>
                </div>
                <div className="space-y-2">
                  <h2 className="text-xl font-bold">Course Details</h2>
                  <p className="text-gray-500 dark:text-gray-400">
                    {singleCourse.description}
                  </p>
                </div>
                <div className="space-y-2">
                  <h2 className="text-xl font-bold">Instructor</h2>
                  <div className="flex items-center space-x-4">
                    <p className="text-gray-500 dark:text-gray-400">
                      {singleCourse.trainerName}
                    </p>
                  </div>
                </div>
                <div className="flex justify-end">
                  <Button>Attempt Quiz</Button>
                </div>
              </div>
            </div>
            <div className="mt-5 gap-8 max-w-6xl mx-auto py-6 px-4 md:px-6 border border-gray-200">
              <FileViewer
                courseId={singleCourse.id}
                courseName={singleCourse.title}
                projectName={singleCourse.projectName}
                departmentName={singleCourse.departmentName}
                courseContent={singleCourse.content}
                fetchCourse={fetchCourse}
              />
            </div>
            <div className="max-w-6xl mx-auto px-4 md:px-6 py-6">
              <h2 className="text-xl font-bold mb-4">Course Comments</h2>
              <div className="space-y-4">
                <div className="flex items-start space-x-4">
                  <div className="space-y-2">
                    <div className="flex items-center space-x-2">
                      <p className="font-medium">Jane Doe</p>
                      <p className="text-gray-500 dark:text-gray-400 text-sm">
                        2 days ago
                      </p>
                    </div>
                    <p className="text-gray-500 dark:text-gray-400">
                      I really enjoyed this course! The instructor was
                      knowledgeable and the content was well-structured. I feel
                      much more confident in my web development skills after
                      completing this.
                    </p>
                  </div>
                </div>
                <div className="flex items-start space-x-4">
                  <div className="space-y-2">
                    <div className="flex items-center space-x-2">
                      <p className="font-medium">Michael Johnson</p>
                      <p className="text-gray-500 dark:text-gray-400 text-sm">
                        1 week ago
                      </p>
                    </div>
                    <p className="text-gray-500 dark:text-gray-400">
                      The course was a great introduction to web development.
                      The hands-on projects really helped solidify the concepts
                      I learned. I would definitely recommend this to anyone
                      interested in getting started with web development.
                    </p>
                  </div>
                </div>
              </div>
            </div>
          </>
        )}
    </>
  );
}

slice.ts

import { courseService } from "@/features/course/courseService";
import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit";
import { toast } from "sonner";
import { ErrorResponse } from "react-router-dom";
import { Response } from "@/types/response";
import { AxiosProgressEvent, CancelToken, CancelTokenSource } from "axios";

interface UserInformation {
  id: string;
  email: string;
  username: string;
}

interface Project {
  id: number;
  projectName: string;
}

interface ProjectData {
  project: Project;
}

interface UserProject {
  user: UserInformation;
}

interface CourseState {
  course: any;
  allCourse: Course[];
  isError: boolean;
  isSuccess: boolean;
  isLoading: boolean;
  message?: string;
  projectList?: ProjectData[];
  userList?: Array<{
    UserProject: UserProject[];
  }>;
}

interface Course {
  id: string;
  title: string;
  thumbnail: string;
  startDate: Date;
  endDate: Date;
  description: string;
  trainer: {
    username: string;
  };
  UserCourse?: {
    user: {
      id: string;
      email: string;
      username: string;
    };
  };
  project?: {
    projectName: string;
    departmentId: number;
  };
}

export const getCourseById = createAsyncThunk<
  Response<Course>,
  { courseId: string; prefix: string },
  { rejectValue: { message: string } }
>("courses/course", async (params, thunkAPI) => {
  try {
    return await courseService.getACourse(params);
  } catch (error) {
    const err = error as ErrorResponse;
    return thunkAPI.rejectWithValue({ message: err.data.message });
  }
});

const initialState: CourseState = {
  course: {},
  allCourse: [],
  isError: false,
  isSuccess: false,
  isLoading: false,
};

export const courseSlice = createSlice({
  name: "courses",
  initialState: initialState,
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(getCourseById.pending, (state) => {
        state.isLoading = true;
      })
      .addCase(
        getCourseById.fulfilled,
        (state, action: PayloadAction<Response<Course>>) => {
          state.isLoading = false;
          state.isError = false;
          state.course = action.payload.data;
          state.isSuccess = true;
        }
      )
      .addCase(getCourseById.rejected, (state, action) => {
        state.isLoading = false;
        state.isError = true;
        state.isSuccess = false;
        state.message = action.payload?.message;
        toast.error(state.message);
      })
  },
});

export default courseSlice.reducer;

store.ts

import { configureStore } from "@reduxjs/toolkit";
import authReducer from "@/features/auth/authSlice";
import courseReducer from "@/features/course/courseSlice";

export const store = configureStore({
  reducer: {
    auth: authReducer,
    courses: courseReducer,
  },
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: false,
    }),
});

// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>;
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch;

I’m trying to create loading effect when fetching data using react redux toolkits. But whenever i render it leads to infinity dispatch.

New contributor

Nguyễn Tùng Sơn is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

1

LEAVE A COMMENT