My app is a simple bookstore, I am using this API https://www.notion.so/Bookstore-API-51ea269061f849118c65c0a53e88a739 I am able to get the books and post books whitout problems, but when I delete the books it works in the UI but when I refresh the page the books just come back,so is not deleting the books from the API.
this is my reducer
const bookReducer = (state = initialState, action) => {
switch (action.type) {
case ADD:
return [
...state, action.book,
];
case REMOVE:
return state.filter((book) => book.item_id !== action.book.item_id);
case FETCH_BOOKS: {
const bookList = [];
Object.entries(action.books).forEach(([key, value]) => bookList.push({
item_id: key,
title: value[0].title,
author: value[0].author,
category: value[0].category,
}));
return bookList;
}
default:
return state;
}
};
and this is my actions for deleting the books : first my remove action
export const removeBook = (book) => ({
type: REMOVE,
book,
});
and then my action using thunks
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;
});
now here is the function where I call the deleteBooks
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})}
>
Remove
</button>
</>
);
}
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.
deleteHandler
is called and { item_id }
value is passed.deleteHander
names argument id
deleteHandler
dispatches the deleteBooks
action with { item_id }
as argument, now named bookId
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>
</>
);
}
deleteBooks
consumes bookId
and passes it directly in the DELETE requests URL path, and in the request bodyIt 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.
deleteBooks
dispatches removeBook
and passes bookId
, still { item_id }
, as a payloadexport 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;
}
);
removeBook
takes { item_id }
as a payload valueexport const removeBook = (book) => ({ // (6) { item_id }
type: REMOVE,
book, // (6) { item_id } -> action.book.item_id
});
bookReducer
reducer function handles REMOVE
action and correctly accesses into the action payload to get the nested item_id
property 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)
...
}
};
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;
}
);
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
);
...
}
};
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.