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
deleteHandler
is called and{ item_id }
value is passed. deleteHander
names argumentid
deleteHandler
dispatches thedeleteBooks
action 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>
</>
);
}
deleteBooks
consumesbookId
and 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.
deleteBooks
dispatchesremoveBook
and 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;
}
);
removeBook
takes{ item_id }
as a payload value
export const removeBook = (book) => ({ // (6) { item_id }
type: REMOVE,
book, // (6) { item_id } -> action.book.item_id
});
bookReducer
reducer function handlesREMOVE
action and correctly accesses into the action payload to get the nesteditem_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)
...
}
};
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.