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