A request takes time. Between the click and the answer, the button still works, so a quick second click sends a second POST. A pending state fixes this: a state that is true while the request is on its way. The button reads it, turns itself off and says what is happening.
A "Place order" button could do the same:
const [placing, setPlacing] = useState(false);
<button disabled={placing} onClick={placeOrder}>
{placing ? "Placing…" : "Place order"}
</button>
Set the state to true before the request, and back to false in finally, which runs after a good answer and after an error. When the server says no, App's addToCart (in the src/App.jsx tab) throws an Error with the server's own words, so the card can catch them and show them.
Your page must do this:
> click "Add to cart" in "Mug"
POST /api/cart {"product_id": 1, "quantity": 1} ...
while waiting: Mug's button → Adding… (disabled)
answer → 201 Mug's button: Add to cart, cart: Mug × 1 $12
> click "Add to cart" in "Lamp"
POST /api/cart {"product_id": 3, "quantity": 1} → 201
> click "Add to cart" in "Lamp"
POST /api/cart {"product_id": 3, "quantity": 1} → 409 under the Lamp: alert "Only 1 left"
Your task: in src/ProductCard.jsx, write the card's button with an adding state that turns it off and shows "Adding…" while the request waits, and a message state that shows the server's message in an alert under the card.
Assignment
Follow the Coding Tutorial to give each product card a pending state, a button that waits for its answer, and an alert for the server's message.
Hintawait onAdd(product) waits for the answer. Put setAdding(false) in finally, so it runs after a 201 and after a 409.
Free plan: 30 tutor messages. PRO is unlimited.
