How can I do in React so that once a product is added to the cart if that product has already been added before it does not repeat a new object?

Viewed 34

[ I need to avoid the duplication of an object that has already been added to the cart, so that once I add it again, I only add the quantity property of the previous object, which would be the same ]

[CartContext.js]

import React, { createContext, useState } from "react";

export const CarritoContext = createContext();

export default function CartContext({ children }) {
  const [addToCarrito, setAddToCarrito] = useState([]);

[This is the function that I cannot modify so that it complies with a certain rule of not adding duplicates of an object that has already been added to the cart, only increasing its quantity of the already added object]

  function addItem(item, quantity) {
    setAddToCarrito(
      addToCarrito.filter((elemento, pos) => {
        if (elemento.item.id === item.id) {
          addToCarrito[pos].quantity += quantity;
          return false;
        }
        return true;
      })
    );

    if (quantity === 0) {
      setAddToCarrito([...addToCarrito]);
    } else {
      setAddToCarrito([...addToCarrito, { item, quantity }]);
    }
  }

  function clear() {
    setAddToCarrito([]);
  }

  function removeItem(itemId) {
    const newItems = addToCarrito.filter((item) => item.item.id !== itemId);
    setAddToCarrito(newItems);
  }

  console.log(addToCarrito);

  return (
    <>
      <CarritoContext.Provider
        value={{ addToCarrito, setAddToCarrito, clear, addItem, removeItem }}
      >
        {children}
      </CarritoContext.Provider>
    </>
  );
}

[ItemAside.js]

import React, { useState, useContext } from "react";
import { Link } from "react-router-dom";
import ItemCount from "../ItemCount/ItemCount";
import { CarritoContext } from "../../context/CartContext";

const ItemAside = ({ jackets }) => {
  const [quantityCarro, setQuantityCarro] = useState(0);
  const [branded] = useState(jackets.brand);

  let { addToCarrito } = useContext(CarritoContext);
  let { setAddToCarrito } = useContext(CarritoContext);
  let { clear } = useContext(CarritoContext);
  let { addItem } = useContext(CarritoContext);
  let { removeItem } = useContext(CarritoContext);

  const onAdd = (cantidadCarro) => {
    setQuantityCarro(cantidadCarro);
    setAddToCarrito([
      ...addToCarrito,
      { item: jackets, quantity: cantidadCarro }
    ]);
    addItem(jackets, cantidadCarro);
  };

  return (
    <div className="container-vertical">
      <aside style={{ width: "100%" }}>
        <div className="container-cuadrado">
          <div>
            <h3>${jackets.price}</h3>
          </div>
          <div>
            <p>and FREE Returns</p>
          </div>
          <div>
            <p>
              Delivery for <strong>$39.99</strong>
            </p>
            <p>
              between <strong>17 - 30 April</strong>
            </p>
          </div>
          <div>
            <small>
              <span className="ubicacion"></span> Deliver to Argentina
            </small>
          </div>

          <div>
            {quantityCarro ? (
              <>
                <Link to="/cart">
                  <button className="close zbutton">Buy now</button>
                </Link>
                <p onClick={clear}>Limpiar carro </p>
                <br />
                <br />
                <p onClick={() => removeItem(jackets.id)}>
                  {`Quitar ${jackets.name} de el carro`}
                </p>
              </>
            ) : (
              <ItemCount
                stock={jackets.stock}
                branded={branded}
                initial={0}
                onAdd={onAdd}
              />
            )}
          </div>

          <div>
            <div className="celwidget">
              <div className="a-section a-spacing-small a-text-left celwidget">
                <span className="a-declarative">
                  <span className="aok-align-center">
                    <img
                      alt=""
                      src="https://images-na.ssl-images-amazon.com/images/G/30/x-locale/checkout/truespc/secured-ssl._CB485936936_.png"
                      height="15px"
                    />
                  </span>
                  <span className="a-letter-space"></span>
                  <span
                    className="dataspan"
                    style={{
                      cursor: "pointer",
                      color: "#0099C0",
                    }}
                    data-hover="We work hard to protect your security and privacy. Our payment security system encrypts your information during transmission. We don’t share your credit card details with third-party sellers, and we don’t sell your information to others."
                  >
                    Secure transaction
                  </span>
                </span>
              </div>
            </div>
          </div>
          <div className="info-shipping">
            <div>
              <p>Shipping from</p>
              <p>Sold by</p>
            </div>
            <div>
              <p>Carvel</p>
              <p>Carvel</p>
            </div>
          </div>
          <div className="gift-container">
            <label className="control control--checkbox">
              <input type="checkbox" className="checkgift" />
              <small className="small-gift">
                 Add a gift ticket to facilitate returns
              </small>
            </label>
          </div>
        </div>
      </aside>
    </div>
  );
};

export default ItemAside;
1 Answers

I think filter is not the correct function to use. Filter needs to filter object. You are trying to mutate them as well inside. The filter function is immutable which means that it generates a new array instead of an old one.

     addToCarrito.filter((elemento, pos) => {
        if (elemento.item.id === item.id) {
          addToCarrito[pos].quantity += quantity; // <- mutating prev value(bad)
          return false;
        }
        return true;
      })

For this task is better to use an object, rather than an array. That will simplify a lot your code.

      const [products, setProducts] = useState({})
    
      const addProduct = (item, quantity) => {
        // if product already in the object - take it, otherwise add new with 0 quatity
        const newProduct = { ...(product[item.id] ?? {...item, quantity: 0}) }
        newProduct.quantity += 1
        setProducts(products => ({
          …products,
          [item.id]: newProduct
         }) 
        })
      }
    
      // here the array of your products, f.e. for iteration
      const productList = React.useMemo(() => Object.values(products), [products])

Related