'Get Quantity for each product using React Context

I have my state for the Cart.

  const [cart, setCart] = useState([]);

I have these functions in my context that adds and remove products respectively.

// Add Product to cart and save to current user {}
  const addProduct = (product) => {
    const exist = cart.find((x) => x.id === product.id);
    if (exist) {
      setCart(
        cart.map((x) =>
          // Expose quantity to any component from here...
          x.id === product.id ? { ...exist, qty: exist.qty + 1 } : x
        )
      );
      
    } else {
      setCart([...cart, { ...product, qty: 1 }]);
      
    }
  };

  const removeProduct = (product) => {
    const exist = cart.find((x) => x.id === product.id);
    if (exist.qty === 1) {
      setCart(cart.filter((x) => x.id !== product.id));
      
    } else {
      setCart(
        cart.map((x) =>
          // Expose quantity to any component from here...
          x.id === product.id ? { ...exist, qty: exist.qty - 1 } : x
        )
      );
    }
  };

The problem now is that I want to get the quantity for each product.

I loop my product like so: note: product is coming from a Firestore.

Shop Page.

{products?.map((docsSnapshot) => {
                const doc = docsSnapshot.data();
                const product = { ...doc, id: docsSnapshot.id };
                const inCart = Boolean(cart.find((el) => el.id === product.id));
                return (
                  <ProductList
                    key={docsSnapshot?.id}
                    docsSnapshot={docsSnapshot}
                    product={product}
                    onAddToCart={(product) => addProduct(product)}
                    onRemoveFromCart={(product) => removeProduct(product)}
                    inCart={inCart}
                  />
                );
              })}

Product List Page

<HStack justify={{ base: "center" }}>
              <IconButton
                size={"xs"}
                aria-label="Remove quantity"
                onClick={() => onRemoveFromCart(product)}
                icon={<RiSubtractLine size={"18px"} />}
              />
              <Text fontSize={"12px"}>{use Quantity here...}</Text>
              <IconButton
                size={"xs"}
                onClick={() => onAddToCart(product)}
                aria-label="Add Quantity"
                colorScheme={"success"}
                icon={<RiAddLine size={"18px"} />}
              />
            </HStack>

Product Details Page

<Text fontSize={"12px"}>{use quantity here too...}</Text>.

Lastly, I also want to check the cart has that product already.

Something like

const inCart = // Check if the item already exists in the cart's array 


Solution 1:[1]

You need to create a cart context initial something like this

// In CartContext.js

const CartContext = React.createContext({
    items: [],
    addItem: (item) => {},
    deleteItem: (id) => {},
});

export default CartContext;

Then for your provider you can do something like below.

// In CartProvider.js
const CartProvider = (props) =>{ 

 const [cart, setCart] = useState([]);

... 
your add and remove functions for the product
...

const cartContext = {
        items: cart,
        
        addItem: addProduct,
        removeItem: removeProduct,
    };

    return (
        <CartContext.Provider value={cartContext}>
            {props.children}
        </CartContext.Provider>
    );
}

Then wrap the components inside which you want your cart value.

After which, Suppose you want to use the value in Shop page. Import the cartcontext and get a instance of it.

    const cartCtx = useContext(CartContext);

Now the context data should be available.

Now you can access the data normally like cartCtx.items and do whatever you like. For adding and removing try to pass the data using a reference function like (change the values accordingly based off of the data) this ( I have added for removing or deleting product, you can do similarly for adding product)

const handleItemDelete = (id) => {
        cartCtx.removeItem(productId);
    };


const cartItemsList = cartCtx.items.map((item) => {
        return (
            <CartItem
                key={item._id ?? item.id} 
                {...props}

                ...your custom fields

                {...item}
                handleItemDelete={handleItemDelete}
            />
        );
    });
...

Also, If you are doing more complicated operations like a e-commerce site, probably make use of useReducer or Redux toolkit (preferred, since I did notice some performance issues while I was doing the same with context but need to check further on this)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 innocent