I am trying to make a simple e-commerce app. When a user goes to cart section and try to increase or decrease quantity, it changes the state but remains same on the page. I need to go back and go cart again to update. How can it change dynamically?
function CardItem() {
const {cart, setCart} = useContext(ProductContext)
const addQuantity = (cartItem) => {
return cart.map((item) => (
cartItem.id === item.id ? item.quantity = item.quantity 1 : item
))
}
const removeQuantity = (cartItem) => {
cart.map((item) => (
cartItem.id === item.id ? item.quantity = item.quantity - 1 : item
))
}
return (
{
cart.map((cartItem) => (
<tr key={cartItem.id}>
<td >
<div >
<div >
<button className='increase' onClick={() => removeQuantity(cartItem)}>
-</button>
{cartItem.quantity}
<button className='increase' onClick={() => addQuantity(cartItem)}> </button>
</div>
</div>
</td>
</tr>
))
})}
CodePudding user response:
Issue
- The code in your snippet isn't calling
setCart
to update any state. - The add/remove quantity handlers are just mutating the
cart
state object. Don't mutate React state.
Solution
Use the setCart
state updater function to enqueue a state update to trigger a rerender to view the updated state. Use a functional state update to correctly update from the previous state and remember that you should shallow copy any state that is being updated.
Example:
function CardItem() {
const { cart, setCart } = useContext(ProductContext);
const addQuantity = (cartItem) => {
setCart(cart => cart.map(item => cartItem.id === item.id
? { // <-- new item object reference
...item, // <-- shallow copy item
quantity: item.quantity 1, // <-- update property
}
: item
));
};
const removeQuantity = (cartItem) => {
setCart(cart => cart.map(item => cartItem.id === item.id
? {
...item,
quantity: item.quantity - 1,
}
: item
));
};
return (
{cart.map((cartItem) => (
<tr key={cartItem.id}>
<td >
<div >
<div >
<button className='increase' onClick={() => removeQuantity(cartItem)}>
-
</button>
{cartItem.quantity}
<button className='increase' onClick={() => addQuantity(cartItem)}>
</button>
</div>
</div>
</td>
</tr>
))
});
}
Since adding/removing is essentially the same action, it's common to use a single function to handle both and pass in the quantity you want to adjust by.
function CardItem() {
const { cart, setCart } = useContext(ProductContext);
const addQuantity = (id, amount) => () => {
setCart(cart => cart.map(item => cartItem.id === id
? {
...item,
quantity: item.quantity amount,
}
: item
));
};
return (
{cart.map((cartItem) => (
<tr key={cartItem.id}>
<td >
<div >
<div >
<button
className='increase'
onClick={addQuantity(cartItem.id, -1)}
>
-
</button>
{cartItem.quantity}
<button
className='increase'
onClick={addQuantity(cartItem.id, 1)}
>
</button>
</div>
</div>
</td>
</tr>
))
});
}
CodePudding user response:
I will recommend using reducers with context to manage state. Something like below a new CartReducer with ADD and REMOVE item etc. actions
const [state, dispatch] = useReducer(CartReducer, initalState);
const addToCart = (id) => {
dispatch({ type: ADD, payload: id});
};
const showHideCart = () => {
dispatch({ type: SHOW, payload:'' });
};
const removeItem = (id) => {
dispatch({ type: REMOVE, payload: id });
};
You can refer this project if it helps shopping-cart-with-context-api