2

I'm implementing a shopping cart for a ecommerce website. The shopping cart is a state variable shopCart represented by an array of objects. Each object contains information about a product, such as title and price. I am trying to implement a remove button, which is actually doing what is intended from it, which is to remove items from the shopCart state, but the changes are not represented on the screen render. I can empty the cart, but the screen still shows the products. Here is the main code of the shopping cart page:

return (
            <div class={styles.container}>
                <h1>Product</h1><h1>Quantity</h1><h1>Unit price</h1><h1>Total price</h1><div></div>
                {
                    shopCart.map((product, i, array)   => <CartItem key={product.id} product={product} index={i} array={array}/>)
                }
            </div>
        )

And here is the implementation of CartItem.js

const CartItem = (props) => {
    let { shopCart, setShopCart } = useContext(Context);
    let product = props.product;

// takes the identification of a shopping cart product and removes it from the cart
    const decrease = (element) => {
        shopCart.forEach((el, i) => {
            if (el.hasOwnProperty('id')) {
                if (el.id === element) {
                    let aux = shopCart;
                    aux.splice(i, 1);
                    setShopCart(aux);
                }
            }
        })
    }

    return (
        <div>
            <img src={product.image}></img>
            <h1>{product.quantity}</h1>
            <h1>{product.price}</h1>
            <h1>{product.price * product.quantity}</h1>
            <button onClick={() => {
                decrease(product.id);
            }}>Remove</button>
        </div>
    )
}

Why isn't it rendering the cart correctly, even though the cart items are being removed after each click of the remove button ?

Emile Bergeron
  • 14,368
  • 4
  • 66
  • 111
  • 1
    Does this answer your question? [Delete item from state array in react](https://stackoverflow.com/questions/36326612/delete-item-from-state-array-in-react) – Emile Bergeron Oct 08 '20 at 23:10

3 Answers3

4

Issue

You are mutating state. You save a reference to state, mutate it, then save it back into state, so the array reference never changes. React uses shallow equality when checking if state or props update.

const decrease = (element) => {
    shopCart.forEach((el, i) => {
        if (el.hasOwnProperty('id')) {
            if (el.id === element) {
                let aux = shopCart; // <-- Saved state ref
                aux.splice(i, 1);   // <-- mutation
                setShopCart(aux);   // <-- Saved ref back to state
            }
        }
    })
}

Solution

The correct way to update arrays in react state is to copy the array elements into a new array reference. This can be easily accomplished by filtering the current cart by item id. I also suggest changing the argument name so it is clearer what it represents.

const decrease = (id) => {
  setShopCart(shopCart => shopCart.filter(item => item.id !== id));
}
Drew Reese
  • 43,833
  • 5
  • 21
  • 43
2

You're modifying the shopCart (aux is a reference) directly which is both the context and the collection that you're iterating over. You need to make sure you're updating a copy of the shopping cart and resetting the context. Minimally, you can do the following:

    const decrease = (element) => {
        shopCart.forEach((el, i) => {
            if (el.hasOwnProperty('id')) {
                if (el.id === element) {
                    let aux = shopCart.slice(); // makes a copy
                    aux.splice(i, 1);
                    setShopCart(aux);
                }
            }
        })
    }

However, I suggest using the approach Drew recommended. The current approach isn't ideal.

jvgaeta
  • 93
  • 4
1

The solution is much simpler than you think. You can use array.filter to remove the matching product by id.

const CartItem = (props) => {
  const { product} = props;
  let { shopCart, setShopCart } = useContext(Context);

  // takes the identification of a shopping cart product and removes it from the cart
  const handleClick = (e) => {
    const filteredShopCart = shopCart.filter(item => item.id !== product.id);
    setShopCart(filteredShopCart);
  };

  return (
    <div>
      <img src={product.image}></img>
      <h1>{product.quantity}</h1>
      <h1>{product.price}</h1>
      <h1>{product.price * product.quantity}</h1>
      <button onClick={handleClick}>Remove</button>
    </div>
  );
};
Benjamin
  • 1,634
  • 9
  • 17