This question is also from the library management system I am working on, there seems to be some problem with my slice function
<code>import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
const URL = "http://localhost:5000/api/books/";
//get books from database
export const getBooks = createAsyncThunk(
"books/getAll",
async (_, thunkAPI) => {
try {
const token = thunkAPI.getState().auth.user.token;
const config = {
headers: {
Authorization: `Bearer ${token}`,
},
};
const response = await axios.get(URL, config);
return response.data;
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
return thunkAPI.rejectWithValue(message);
}
}
);
//adds a new book
export const addBook = createAsyncThunk(
"books/add",
async (bookData, thunkAPI) => {
try {
const token = thunkAPI.getState().auth.user.token;
const config = {
headers: {
Authorization: `Bearer ${token}`,
},
};
const response = await axios.post(URL, bookData, config);
return response.data;
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
return thunkAPI.rejectWithValue(message);
}
}
);
//edits a book
export const editBook = createAsyncThunk(
"books/edit",
async (bookData, bookId, thunkAPI) => {
try {
const token = thunkAPI.getState().auth.user.token;
const config = {
headers: {
Authorization: `Bearer ${token}`,
},
};
const response = await axios.put(URL + bookId, bookData, config);
return response.data;
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
return thunkAPI.rejectWithValue(message);
}
}
);
//deletes a book
export const deleteBook = createAsyncThunk(
"books/delete",
async (bookId, thunkAPI) => {
try {
const token = thunkAPI.getState().auth.user.token;
const config = {
headers: {
Authorization: `Bearer ${token}`,
},
};
const response = await axios.delete(URL + bookId, config);
return response.data;
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
return thunkAPI.rejectWithValue(message);
}
}
);
const initialState = {
isLoading: false,
isError: false,
isSuccess: false,
books: [],
message: "",
};
const bookSlice = createSlice({
name: "bookSlice",
initialState,
reducers: {
reset: (state) => initialState,
},
extraReducers: (builder) => {
builder
//get all books
.addCase(getBooks.pending, (state) => {
state.isLoading = true;
})
.addCase(getBooks.fulfilled, (state, action) => {
state.isLoading = false;
state.isSuccess = true;
state.books = action.payload;
})
.addCase(getBooks.rejected, (state, action) => {
state.isLoading = false;
state.isError = true;
state.message = action.payload;
})
//add new book
.addCase(addBook.pending, (state) => {
state.isLoading = true;
})
.addCase(addBook.fulfilled, (state, action) => {
state.isLoading = false;
state.isSuccess = true;
state.books.push(action.payload);
})
.addCase(addBook.rejected, (state, action) => {
state.isLoading = false;
state.isError = true;
state.message = action.payload;
})
//update book
.addCase(editBook.pending, (state) => {
state.isLoading = true;
})
.addCase(editBook.fulfilled, (state, action) => {
state.isLoading = false;
state.isSuccess = true;
const index = state.books.findIndex(
(book) => book._id === action.payload._id
);
if (index !== -1) {
state.books[index] = action.payload;
}
})
.addCase(editBook.rejected, (state, action) => {
state.isLoading = false;
state.isError = true;
state.message = action.payload;
})
//delete book
.addCase(deleteBook.fulfilled, (state, action) => {
state.books = state.books.filter(
(book) => book._id !== action.payload.id
);
});
},
});
export const { reset } = bookSlice.actions;
export default bookSlice.reducer;
</code>
<code>import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
const URL = "http://localhost:5000/api/books/";
//get books from database
export const getBooks = createAsyncThunk(
"books/getAll",
async (_, thunkAPI) => {
try {
const token = thunkAPI.getState().auth.user.token;
const config = {
headers: {
Authorization: `Bearer ${token}`,
},
};
const response = await axios.get(URL, config);
return response.data;
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
return thunkAPI.rejectWithValue(message);
}
}
);
//adds a new book
export const addBook = createAsyncThunk(
"books/add",
async (bookData, thunkAPI) => {
try {
const token = thunkAPI.getState().auth.user.token;
const config = {
headers: {
Authorization: `Bearer ${token}`,
},
};
const response = await axios.post(URL, bookData, config);
return response.data;
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
return thunkAPI.rejectWithValue(message);
}
}
);
//edits a book
export const editBook = createAsyncThunk(
"books/edit",
async (bookData, bookId, thunkAPI) => {
try {
const token = thunkAPI.getState().auth.user.token;
const config = {
headers: {
Authorization: `Bearer ${token}`,
},
};
const response = await axios.put(URL + bookId, bookData, config);
return response.data;
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
return thunkAPI.rejectWithValue(message);
}
}
);
//deletes a book
export const deleteBook = createAsyncThunk(
"books/delete",
async (bookId, thunkAPI) => {
try {
const token = thunkAPI.getState().auth.user.token;
const config = {
headers: {
Authorization: `Bearer ${token}`,
},
};
const response = await axios.delete(URL + bookId, config);
return response.data;
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
return thunkAPI.rejectWithValue(message);
}
}
);
const initialState = {
isLoading: false,
isError: false,
isSuccess: false,
books: [],
message: "",
};
const bookSlice = createSlice({
name: "bookSlice",
initialState,
reducers: {
reset: (state) => initialState,
},
extraReducers: (builder) => {
builder
//get all books
.addCase(getBooks.pending, (state) => {
state.isLoading = true;
})
.addCase(getBooks.fulfilled, (state, action) => {
state.isLoading = false;
state.isSuccess = true;
state.books = action.payload;
})
.addCase(getBooks.rejected, (state, action) => {
state.isLoading = false;
state.isError = true;
state.message = action.payload;
})
//add new book
.addCase(addBook.pending, (state) => {
state.isLoading = true;
})
.addCase(addBook.fulfilled, (state, action) => {
state.isLoading = false;
state.isSuccess = true;
state.books.push(action.payload);
})
.addCase(addBook.rejected, (state, action) => {
state.isLoading = false;
state.isError = true;
state.message = action.payload;
})
//update book
.addCase(editBook.pending, (state) => {
state.isLoading = true;
})
.addCase(editBook.fulfilled, (state, action) => {
state.isLoading = false;
state.isSuccess = true;
const index = state.books.findIndex(
(book) => book._id === action.payload._id
);
if (index !== -1) {
state.books[index] = action.payload;
}
})
.addCase(editBook.rejected, (state, action) => {
state.isLoading = false;
state.isError = true;
state.message = action.payload;
})
//delete book
.addCase(deleteBook.fulfilled, (state, action) => {
state.books = state.books.filter(
(book) => book._id !== action.payload.id
);
});
},
});
export const { reset } = bookSlice.actions;
export default bookSlice.reducer;
</code>
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
const URL = "http://localhost:5000/api/books/";
//get books from database
export const getBooks = createAsyncThunk(
"books/getAll",
async (_, thunkAPI) => {
try {
const token = thunkAPI.getState().auth.user.token;
const config = {
headers: {
Authorization: `Bearer ${token}`,
},
};
const response = await axios.get(URL, config);
return response.data;
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
return thunkAPI.rejectWithValue(message);
}
}
);
//adds a new book
export const addBook = createAsyncThunk(
"books/add",
async (bookData, thunkAPI) => {
try {
const token = thunkAPI.getState().auth.user.token;
const config = {
headers: {
Authorization: `Bearer ${token}`,
},
};
const response = await axios.post(URL, bookData, config);
return response.data;
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
return thunkAPI.rejectWithValue(message);
}
}
);
//edits a book
export const editBook = createAsyncThunk(
"books/edit",
async (bookData, bookId, thunkAPI) => {
try {
const token = thunkAPI.getState().auth.user.token;
const config = {
headers: {
Authorization: `Bearer ${token}`,
},
};
const response = await axios.put(URL + bookId, bookData, config);
return response.data;
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
return thunkAPI.rejectWithValue(message);
}
}
);
//deletes a book
export const deleteBook = createAsyncThunk(
"books/delete",
async (bookId, thunkAPI) => {
try {
const token = thunkAPI.getState().auth.user.token;
const config = {
headers: {
Authorization: `Bearer ${token}`,
},
};
const response = await axios.delete(URL + bookId, config);
return response.data;
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
return thunkAPI.rejectWithValue(message);
}
}
);
const initialState = {
isLoading: false,
isError: false,
isSuccess: false,
books: [],
message: "",
};
const bookSlice = createSlice({
name: "bookSlice",
initialState,
reducers: {
reset: (state) => initialState,
},
extraReducers: (builder) => {
builder
//get all books
.addCase(getBooks.pending, (state) => {
state.isLoading = true;
})
.addCase(getBooks.fulfilled, (state, action) => {
state.isLoading = false;
state.isSuccess = true;
state.books = action.payload;
})
.addCase(getBooks.rejected, (state, action) => {
state.isLoading = false;
state.isError = true;
state.message = action.payload;
})
//add new book
.addCase(addBook.pending, (state) => {
state.isLoading = true;
})
.addCase(addBook.fulfilled, (state, action) => {
state.isLoading = false;
state.isSuccess = true;
state.books.push(action.payload);
})
.addCase(addBook.rejected, (state, action) => {
state.isLoading = false;
state.isError = true;
state.message = action.payload;
})
//update book
.addCase(editBook.pending, (state) => {
state.isLoading = true;
})
.addCase(editBook.fulfilled, (state, action) => {
state.isLoading = false;
state.isSuccess = true;
const index = state.books.findIndex(
(book) => book._id === action.payload._id
);
if (index !== -1) {
state.books[index] = action.payload;
}
})
.addCase(editBook.rejected, (state, action) => {
state.isLoading = false;
state.isError = true;
state.message = action.payload;
})
//delete book
.addCase(deleteBook.fulfilled, (state, action) => {
state.books = state.books.filter(
(book) => book._id !== action.payload.id
);
});
},
});
export const { reset } = bookSlice.actions;
export default bookSlice.reducer;
and here is my EditBook component
<code>import React from "react";
import { Formik, Form, Field, ErrorMessage } from "formik";
import * as Yup from "yup";
import { useDispatch } from "react-redux";
import { editBook } from "../../../actions/bookSlice";
const EditBook = ({ book }) => {
const dispatch = useDispatch();
const { title, author, category, isbn, refId } = book;
// Validation schema using Yup
const validationSchema = Yup.object().shape({
title: Yup.string().required("Title is required"),
author: Yup.string().required("Author is required"),
category: Yup.string().required("Category is required"),
isbn: Yup.string().required("isbn is required"),
refId: Yup.string().required("Reference ID is required"),
});
// Initial form values
const initialValues = {
title,
author,
category,
isbn,
refId,
};
// Form submission handler
const handleSubmit = (values) => {
// Dispatch action to create book
dispatch(editBook(values, book._id));
// Reset form after submission
resetForm();
// Close modal
document.getElementById("edit_book_modal").close();
};
return (
<div>
{/* Button to open the modal */}
<button
className="btn btn-warning"
onClick={() => document.getElementById("edit_book_modal").showModal()}
>
Edit Book
</button>
{/* Modal */}
<dialog id="edit_book_modal" className="modal">
<div className="modal-box">
<h3 className="font-bold text-lg">Edit Book</h3>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={handleSubmit}
>
{({ errors, touched }) => (
<Form>
{/* Title */}
<div className="py-2">
<label htmlFor="title" className="block font-medium">
Title
</label>
<Field
type="text"
id="title"
name="title"
className="input input-bordered w-full"
/>
<ErrorMessage
name="title"
component="p"
className="text-red-500"
/>
</div>
{/* Author */}
<div className="py-2">
<label htmlFor="author" className="block font-medium">
Author
</label>
<Field
type="text"
id="author"
name="author"
className="input input-bordered w-full"
/>
<ErrorMessage
name="author"
component="p"
className="text-red-500"
/>
</div>
{/* Category */}
<div className="py-2">
<label htmlFor="category" className="block font-medium">
Category
</label>
<Field
as="select"
id="category"
name="category"
className="input input-bordered w-full"
>
<option value="">Select Category</option>
<option value="Novel">Novel</option>
<option value="Non-fiction">Non-fiction</option>
<option value="Helping-book">Helping-book</option>
</Field>
<ErrorMessage
name="category"
component="p"
className="text-red-500"
/>
</div>
{/* isbn */}
<div className="py-2">
<label htmlFor="isbn" className="block font-medium">
ISBN
</label>
<Field
type="text"
id="isbn"
name="isbn"
className="input input-bordered w-full"
/>
<ErrorMessage
name="isbn"
component="p"
className="text-red-500"
/>
</div>
{/* Reference ID */}
<div className="py-2">
<label htmlFor="refId" className="block font-medium">
Reference ID
</label>
<Field
type="text"
id="refId"
name="refId"
className="input input-bordered w-full"
/>
<ErrorMessage
name="refId"
component="p"
className="text-red-500"
/>
</div>
{/* Modal action buttons */}
<div className="modal-action">
<button
type="submit"
className="btn btn-primary"
>
Save
</button>
<button
type="button"
className="btn ml-2"
onClick={() =>
document.getElementById("edit_book_modal").close()
}
>
Cancel
</button>
</div>
</Form>
)}
</Formik>
</div>
</dialog>
</div>
);
};
export default EditBook;
</code>
<code>import React from "react";
import { Formik, Form, Field, ErrorMessage } from "formik";
import * as Yup from "yup";
import { useDispatch } from "react-redux";
import { editBook } from "../../../actions/bookSlice";
const EditBook = ({ book }) => {
const dispatch = useDispatch();
const { title, author, category, isbn, refId } = book;
// Validation schema using Yup
const validationSchema = Yup.object().shape({
title: Yup.string().required("Title is required"),
author: Yup.string().required("Author is required"),
category: Yup.string().required("Category is required"),
isbn: Yup.string().required("isbn is required"),
refId: Yup.string().required("Reference ID is required"),
});
// Initial form values
const initialValues = {
title,
author,
category,
isbn,
refId,
};
// Form submission handler
const handleSubmit = (values) => {
// Dispatch action to create book
dispatch(editBook(values, book._id));
// Reset form after submission
resetForm();
// Close modal
document.getElementById("edit_book_modal").close();
};
return (
<div>
{/* Button to open the modal */}
<button
className="btn btn-warning"
onClick={() => document.getElementById("edit_book_modal").showModal()}
>
Edit Book
</button>
{/* Modal */}
<dialog id="edit_book_modal" className="modal">
<div className="modal-box">
<h3 className="font-bold text-lg">Edit Book</h3>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={handleSubmit}
>
{({ errors, touched }) => (
<Form>
{/* Title */}
<div className="py-2">
<label htmlFor="title" className="block font-medium">
Title
</label>
<Field
type="text"
id="title"
name="title"
className="input input-bordered w-full"
/>
<ErrorMessage
name="title"
component="p"
className="text-red-500"
/>
</div>
{/* Author */}
<div className="py-2">
<label htmlFor="author" className="block font-medium">
Author
</label>
<Field
type="text"
id="author"
name="author"
className="input input-bordered w-full"
/>
<ErrorMessage
name="author"
component="p"
className="text-red-500"
/>
</div>
{/* Category */}
<div className="py-2">
<label htmlFor="category" className="block font-medium">
Category
</label>
<Field
as="select"
id="category"
name="category"
className="input input-bordered w-full"
>
<option value="">Select Category</option>
<option value="Novel">Novel</option>
<option value="Non-fiction">Non-fiction</option>
<option value="Helping-book">Helping-book</option>
</Field>
<ErrorMessage
name="category"
component="p"
className="text-red-500"
/>
</div>
{/* isbn */}
<div className="py-2">
<label htmlFor="isbn" className="block font-medium">
ISBN
</label>
<Field
type="text"
id="isbn"
name="isbn"
className="input input-bordered w-full"
/>
<ErrorMessage
name="isbn"
component="p"
className="text-red-500"
/>
</div>
{/* Reference ID */}
<div className="py-2">
<label htmlFor="refId" className="block font-medium">
Reference ID
</label>
<Field
type="text"
id="refId"
name="refId"
className="input input-bordered w-full"
/>
<ErrorMessage
name="refId"
component="p"
className="text-red-500"
/>
</div>
{/* Modal action buttons */}
<div className="modal-action">
<button
type="submit"
className="btn btn-primary"
>
Save
</button>
<button
type="button"
className="btn ml-2"
onClick={() =>
document.getElementById("edit_book_modal").close()
}
>
Cancel
</button>
</div>
</Form>
)}
</Formik>
</div>
</dialog>
</div>
);
};
export default EditBook;
</code>
import React from "react";
import { Formik, Form, Field, ErrorMessage } from "formik";
import * as Yup from "yup";
import { useDispatch } from "react-redux";
import { editBook } from "../../../actions/bookSlice";
const EditBook = ({ book }) => {
const dispatch = useDispatch();
const { title, author, category, isbn, refId } = book;
// Validation schema using Yup
const validationSchema = Yup.object().shape({
title: Yup.string().required("Title is required"),
author: Yup.string().required("Author is required"),
category: Yup.string().required("Category is required"),
isbn: Yup.string().required("isbn is required"),
refId: Yup.string().required("Reference ID is required"),
});
// Initial form values
const initialValues = {
title,
author,
category,
isbn,
refId,
};
// Form submission handler
const handleSubmit = (values) => {
// Dispatch action to create book
dispatch(editBook(values, book._id));
// Reset form after submission
resetForm();
// Close modal
document.getElementById("edit_book_modal").close();
};
return (
<div>
{/* Button to open the modal */}
<button
className="btn btn-warning"
onClick={() => document.getElementById("edit_book_modal").showModal()}
>
Edit Book
</button>
{/* Modal */}
<dialog id="edit_book_modal" className="modal">
<div className="modal-box">
<h3 className="font-bold text-lg">Edit Book</h3>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={handleSubmit}
>
{({ errors, touched }) => (
<Form>
{/* Title */}
<div className="py-2">
<label htmlFor="title" className="block font-medium">
Title
</label>
<Field
type="text"
id="title"
name="title"
className="input input-bordered w-full"
/>
<ErrorMessage
name="title"
component="p"
className="text-red-500"
/>
</div>
{/* Author */}
<div className="py-2">
<label htmlFor="author" className="block font-medium">
Author
</label>
<Field
type="text"
id="author"
name="author"
className="input input-bordered w-full"
/>
<ErrorMessage
name="author"
component="p"
className="text-red-500"
/>
</div>
{/* Category */}
<div className="py-2">
<label htmlFor="category" className="block font-medium">
Category
</label>
<Field
as="select"
id="category"
name="category"
className="input input-bordered w-full"
>
<option value="">Select Category</option>
<option value="Novel">Novel</option>
<option value="Non-fiction">Non-fiction</option>
<option value="Helping-book">Helping-book</option>
</Field>
<ErrorMessage
name="category"
component="p"
className="text-red-500"
/>
</div>
{/* isbn */}
<div className="py-2">
<label htmlFor="isbn" className="block font-medium">
ISBN
</label>
<Field
type="text"
id="isbn"
name="isbn"
className="input input-bordered w-full"
/>
<ErrorMessage
name="isbn"
component="p"
className="text-red-500"
/>
</div>
{/* Reference ID */}
<div className="py-2">
<label htmlFor="refId" className="block font-medium">
Reference ID
</label>
<Field
type="text"
id="refId"
name="refId"
className="input input-bordered w-full"
/>
<ErrorMessage
name="refId"
component="p"
className="text-red-500"
/>
</div>
{/* Modal action buttons */}
<div className="modal-action">
<button
type="submit"
className="btn btn-primary"
>
Save
</button>
<button
type="button"
className="btn ml-2"
onClick={() =>
document.getElementById("edit_book_modal").close()
}
>
Cancel
</button>
</div>
</Form>
)}
</Formik>
</div>
</dialog>
</div>
);
};
export default EditBook;
The problem is that when I press the Save button, no API call happens, the network tab in the inspect view shows no request being made, what am I doing wrong?
I tried asking ChatGPT about this too, but to no avail
New contributor
BunKebab is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.