My app is a simple bookstore, I am using this API 
CodePudding user response:
Issue
From what I can read of the code it appears at some point when initiating the process to remove a book you drop the correct id somewhere along the way.
- User clicks "Remove" button and
deleteHandleris called and{ item_id }value is passed. deleteHandernames argumentiddeleteHandlerdispatches thedeleteBooksaction with{ item_id }as argument, now namedbookId
function Book({ title, author, item_id, category }) {
const dispatch = useDispatch();
const deleteHandler = (id) => { // (2) id == { item_id }
console.log(id);
dispatch(deleteBooks(id)); // (3)
}
return (
<>
<span>
{title}
</span>
<br/>
<span>
{author}
</span>
<br/>
<span>
{category}
</span>
<br/>
<button
type="button"
onClick={() => deleteHandler({ item_id })} // (1)
>
Remove
</button>
</>
);
}
deleteBooksconsumesbookIdand passes it directly in the DELETE requests URL path, and in the request body
It is at this point I'd expect the DELETE request to fail in the backend since the URL is likely incorrect and the body is incorrect. fetch only throws an error on network errors or cancelled requests though, so I am sure the code just continues along error-free. I'm fairly certain response.data is simply undefined.
deleteBooksdispatchesremoveBookand passesbookId, still{ item_id }, as a payload
export const deleteBooks = createAsyncThunk(
REMOVE,
async (bookId, thunkAPI) => {
const response = await fetch(`${BOOK_URL}/${bookId}`, { // (4)
method: 'DELETE',
body: JSON.stringify({
item_id: bookId // (4) { item_id: { item_id } }
}),
headers: {
'Content-Type': 'application/json',
},
});
await thunkAPI.dispatch(removeBook(bookId)); // (5)
return response.data;
}
);
removeBooktakes{ item_id }as a payload value
export const removeBook = (book) => ({ // (6) { item_id }
type: REMOVE,
book, // (6) { item_id } -> action.book.item_id
});
bookReducerreducer function handlesREMOVEaction and correctly accesses into the action payload to get the nesteditem_idproperty and filters the book state array.
const bookReducer = (state = initialState, action) => {
switch (action.type) {
...
case REMOVE:
return state.filter((book) => book.item_id !== action.book.item_id); // (7)
...
}
};
Solution
Payload/argument naming issues aside, you can likely fix the issue in the deleteBooks action by correctly accessing into the passed "book" object to get the nested item_id property.
Example:
export const deleteBooks = createAsyncThunk(
REMOVE,
async (book, thunkAPI) => {
const { item_id } = book // <-- destructure item_id from book object
const response = await fetch(`${BOOK_URL}/${item_id}`, { // <-- pass in URL path
method: 'DELETE',
body: JSON.stringify({ item_id }), // <-- pass in body
headers: {
'Content-Type': 'application/json',
},
});
await thunkAPI.dispatch(removeBook(book)); // <-- pass book object
return response.data;
}
);
Suggestions
Use consistent naming throughout code
I suggest editing all these functions/actions/reducers/etc to consistently name the referenced objects throughout the code. It's much easier to keep and maintain the mental mapping of the data flowing through/across the app.
Since the Book component starts with just a book id, i.e. the item_id prop, then passing a "book id" value around makes a bit more sense.
function Book({ title, author, item_id, category }) {
const dispatch = useDispatch();
const deleteHandler = (id) => {
console.log(id);
dispatch(deleteBooks(id));
}
return (
<>
<span>
{title}
</span>
<br/>
<span>
{author}
</span>
<br/>
<span>
{category}
</span>
<br/>
<button
type="button"
onClick={() => deleteHandler(item_id)} // <-- pass book id
>
Remove
</button>
</>
);
}
export const deleteBooks = createAsyncThunk(
REMOVE,
async (bookId, thunkAPI) => {
const response = await fetch(`${BOOK_URL}/${bookId}`, {
method: 'DELETE',
body: JSON.stringify({ item_id: bookId }),
headers: {
'Content-Type': 'application/json',
},
});
await thunkAPI.dispatch(removeBook(bookId));
return response.data;
}
);
export const removeBook = (bookId) => ({
type: REMOVE,
bookId,
});
const bookReducer = (state = initialState, action) => {
switch (action.type) {
...
case REMOVE:
return state.filter(
(book) => book.item_id !== action.bookId // <-- access book id
);
...
}
};
Check for successful response when removing book
The the fetch response ok* property to ensure the DELETE request was actually successful.
Example:
export const deleteBooks = createAsyncThunk(
REMOVE,
async (bookId, thunkAPI) => {
const response = await fetch(`${BOOK_URL}/${bookId}`, {
method: 'DELETE',
body: JSON.stringify({ item_id: bookId }),
headers: {
'Content-Type': 'application/json',
},
});
if (response.ok) {
thunkAPI.dispatch(removeBook(bookId));
}
return response.data;
}
);
*Note: response.ok is pretty standard but consult the API documentation if success/failure is expressed differently.
