How do I show the data by their product IDs without using reduce?

Title for this problem: Adding the function to remove products from the cart won’t allow me to add more products

I have this function to add products and with their colors, however, once I added the functionality to remove products with their colors, it does not allow me to add a product anymore.

  1. Add products and along with the selected color
  2. Clicking the “-” button should remove the selected color for that product alone. What happens here is that if I’ll add this functionality. it does not allow me to add any products anymore. There’s no error that shows in the console nor does the app crashes as well.

How can I fix this? Thank you

Codesandbox: Add to Cart Sampled 2 with Table - CodeSandbox

Codes:
App.js

export default function App() {
  const [cartItems, setCartItems] = useState([]);
  const [searchList, setSearchList] = useState([]);
  const [searchKey, setSearchKey] = useState("");

  useEffect(() => {
    let x = [...products];
    x = x.filter((y) => {
      return y.prodName
        .toLocaleLowerCase()
        .includes(searchKey.toLocaleLowerCase());
    });
    setSearchList(x);
  }, [searchKey]);

  const handleAdd = (id, name, price, size, cat, color) => {
    console.log("add", id, name, price, size, cat, color);
    const productExist = cartItems.find(
      (item) => item.id === id && item.color === color
    );

    if (productExist) {
      setCartItems(
        cartItems.map((item) =>
          item.id === id && item.color === color
            ? { ...productExist, quantity: productExist.quantity + 1 }
            : item
        )
      );
    } else {
      setCartItems([
        ...cartItems,
        { id, name, price, size, cat, color, quantity: 1 }
      ]);
    }
  };

  const handleRemove = (product) => {
    console.log(product, "remove");
    const ProductExist = cartItems.find(
      (item) => item.id === product.id && item.color === product.color
    );

    if (ProductExist.quantity === 1) {
      setCartItems(cartItems.filter((item) => item.id !== product.id));
    } else {
      setCartItems(
        cartItems.map((item) =>
          item.id === product.id && item.color === product.color
            ? { ...ProductExist, quantity: ProductExist.quantity - 1 }
            : item
        )
      );
    }
  };

  const handleCartClearance = () => {
    setCartItems([]);
  };

  console.log(cartItems);

  return (
    <div className="App">
      <div>
        <input
          placeholder="Search product..."
          value={searchKey}
          onChange={(e) => setSearchKey(e.target.value)}
        />
        {searchList.map((item, index) => (
          <ul key={item.id}>
            <li>
              <b>{item.prodName}</b>
            </li>
            <li>Category: {item.cat}</li>

            <li>Size: {item.size}</li>
            <li>Php {item.price}.00</li>
            {Object.entries(item.colorMap).map((color) => (
              <>
                {color[1] !== 0 ? (
                  <>
                    {color[0]}

                    <button
                      onClick={(e) =>
                        handleAdd(
                          item.id,
                          item.prodName,
                          item.price,
                          item.size,
                          item.cat,
                          color[0]
                        )
                      }
                    >
                      Add
                    </button>
                    {/* <button onClick={(e) => handleAdd(index)}>Add to Cart</button> */}
                  </>
                ) : (
                  <>2</>
                )}
                <br />
              </>
            ))}
          </ul>
        ))}
      </div>
      <Cart
        cartItems={cartItems}
        handleCartClearance={handleCartClearance}
        handleRemove={handleRemove}
        handleAdd={handleAdd}
      />
    </div>
  );
}

Cart.js

const Cart = ({ cartItems, handleCartClearance, handleRemove, handleAdd }) => {
  console.log(cartItems, "items");

  const totalAmount = cartItems.reduce(
    (price, item) => price + item.quantity * item.price,
    0
  );

  return (
    <div>
      Cart page
      {cartItems.length >= 1 && (
        <button onClick={handleCartClearance}>Clear Orders</button>
      )}
      {cartItems.length === 0 && <div>No Items in the cart</div>}
      <div>
        {cartItems.map((item) => (
          <div key={item.id}>
            <li>{item.id}</li>
            <li>{item.name + " " + item.size + "" + item.cat}</li>
            <li>{item.color}</li>
            <li>{item.quantity}</li>

            <li>Total: {parseInt(item.quantity) * parseInt(item.price)}</li>
            <button
              onClick={(e) =>
                handleAdd(
                  item.id,
                  item.prodName,
                  item.price,
                  item.size,
                  item.cat,
                  item.color
                )
              }
            >
              +
            </button>
            <button onClick={handleRemove(item)}>- </button>
          </div>
        ))}

        <div>
          <b>Total Amount :{totalAmount}</b>
        </div>
      </div>
    </div>
  );
};

export default Cart;

This should be

<button onClick={() => handleRemove(item)}>- </button>
1 Like

Thank you so much for the help, it does work now

I have another issue in calculating the quantity upon input of the user. I can’t figure out how I can do this since the input should already have a value of the quantity when the product was added to the cart.

So what I wanted to do was to also calculate the unit price according to what number was entered. I was already able to update the quantity of the product and then calculate its unit price and also its total amount.

My problem now is how can I also allow the user to enter the quantity of the product. And also still retain the functionality to increase and decrease the product.

I have recreated this in codesandbox: Add to Cart Sampled 2 with Table - CodeSandbox


    const Cart = ({ cartItems, handleCartClearance, handleRemove, handleAdd }) => {
      const [inputQty, setInputQty] = useState(0);
      console.log(cartItems, "items");
    
      const totalAmount = cartItems.reduce(
        (price, item) => price + item.quantity * item.price,
        0
      );
    
      const handleSubmit = (e) => {
        e.preventDefault();
        console.log(cartItems, "order");
      };
    
      console.log(
        "test",
        cartItems.reduce((prev, item) => {
          if (!prev[item.id]) prev[item.id] = { ...item, nest: [] };
          prev[item.id].nest.push({ ...item });
          return prev;
        }, {})
      );
    
      return (
        <div>
          <form onSubmit={handleSubmit}>
            Order page
            {cartItems.length >= 1 && (
              <Button onClick={handleCartClearance}>Clear Orders</Button>
            )}
            {cartItems.length === 0 && <div>No Items in the cart</div>}
            <div>
              {Object.entries(
                cartItems.reduce((prev, item) => {
                  if (!prev[item.id]) prev[item.id] = { ...item, nest: [] };
                  prev[item.id].nest.push(item);
                  return prev;
                }, {})
              ).map(([id, obj], idx) => (
                <TableContainer key={id + obj.color} component={Paper}>
                  <Table aria-label="simple table">
                    <TableHead>
                      <TableRow>
                        <TableCell align="center">
                          Product Name: {obj.name + " " + obj.size}
                        </TableCell>
                        <TableCell colspan={3}></TableCell>
                      </TableRow>
                      <TableRow>
                        <TableCell>Color</TableCell>
                        <TableCell>Qty</TableCell>
                        <TableCell></TableCell>
    
                        <TableCell>Unit Price</TableCell>
                      </TableRow>
                    </TableHead>
                    <TableBody>
                      {obj.nest.map((nest, idx) => (
                        <TableRow key={idx}>
                          <TableCell>{nest.color}</TableCell>
                          <TableCell>
                            <input
                              style={{ width: "1rem" }}
                              value={nest.quantity}
                            />
                          </TableCell>
    
                          <TableCell align="right">
                            <IconButton
                              onClick={(e) =>
                                handleAdd(
                                  nest.id,
                                  nest.prodName,
                                  nest.price,
                                  nest.size,
                                  nest.cat,
                                  nest.color
                                )
                              }
                            >
                              <AddIcon color="success" />
                            </IconButton>
    
                            <IconButton onClick={() => handleRemove(nest)}>
                              <RemoveIcon />
                            </IconButton>
                          </TableCell>
                          <TableCell>
                            {Number(nest.quantity) * Number(nest.price)}
                          </TableCell>
                        </TableRow>
                      ))}
                    </TableBody>
                  </Table>
                </TableContainer>
              ))}
             
              <div>
                <b>Total Amount :{totalAmount}</b>
              </div>
              {cartItems.length >= 1 && <Button type="submit">Save Order</Button>}
            </div>
          </form>
        </div>
      );
    };
    
    export default Cart;


You need to provide onChange on input element and provide change handler (just like you did with +/- buttons).

<TableCell>
  <input
    style={{ width: "1rem" }}
    value={nest.quantity}
   onChange={handleChange} <--- this line
  />
</TableCell>
1 Like

I have these cart items that I want to show by their ID, but what I’m using here to show the products by their ID is by reduce. And when I added the table pagination, it does not show data by row correctly. Hence, are there other ways where I could render the cartItems without using reduce? Any help would be appreciated. Thank you

codesandbox: Add to Cart Sampled 2 with Material-UI Table Pagination - CodeSandbox


const Cart = ({ cartItems, handleCartClearance, handleRemove, handleAdd }) => {
  const [inputQty, setInputQty] = useState(0);

  //pagination------------------------------
  const itemsPerPage = 3;
  const [page, setPage] = useState(1);
  const rawPages = cartItems.length / itemsPerPage;
  const noOfPages = Math.ceil(rawPages);

  console.log(noOfPages);

  const handleChange = (event, value) => {
    setPage(value);
  };

  //--------------------------------------------

  console.log(cartItems, "items");

  const totalAmount = cartItems.reduce(
    (price, item) => price + item.quantity * item.price,
    0
  );

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log(cartItems, "order");
  };

  console.log(
    "test",
    cartItems.reduce((prev, item) => {
      if (!prev[item.id]) prev[item.id] = { ...item, nest: [] };
      prev[item.id].nest.push({ ...item });
      return prev;
    }, {})
  );

  return (
    <div>
      <form onSubmit={handleSubmit}>
        Order page
        {cartItems.length >= 1 && (
          <Button onClick={handleCartClearance}>Clear Orders</Button>
        )}
        {cartItems.length === 0 && <div>No Items in the cart</div>}
        <div>
          {Object.entries(
            cartItems.reduce((prev, item) => {
              if (!prev[item.id]) prev[item.id] = { ...item, nest: [] };
              prev[item.id].nest.push(item);
              return prev;
            }, {})
          )
            .slice((page - 1) * itemsPerPage, page * itemsPerPage)
            .map(([id, obj], idx) => (
              <TableContainer key={id + obj.color} component={Paper}>
                <Table
                  aria-label="simple table"
                  size="small"
                  aria-label="a dense table"
                >
                  <TableHead>
                    <TableRow>
                      <TableCell align="center">
                        Product Name: {obj.name + " " + obj.size}
                      </TableCell>
                      <TableCell colspan={3}></TableCell>
                    </TableRow>
                    <TableRow>
                      <TableCell align="center">Color</TableCell>
                      <TableCell>Qty</TableCell>
                      <TableCell></TableCell>

                      <TableCell>Unit Price</TableCell>
                    </TableRow>
                  </TableHead>
                  <TableBody>
                    {obj.nest.map((nest, idx) => (
                      <TableRow key={idx}>
                        <TableCell align="center">{nest.color}</TableCell>
                        <TableCell>
                          <input
                            style={{ width: "1rem" }}
                            value={nest.quantity}
                            onChange={(e) => {
                              if (e.target.value === "0") {
                                return handleRemove(nest);
                              }
                              const re = /^[0-9\b]+$/;
                              if (
                                e.target.value === "" ||
                                re.test(e.target.value)
                              ) {
                                handleAdd(
                                  nest.id,
                                  nest.prodName,
                                  nest.price,
                                  nest.size,
                                  nest.cat,
                                  nest.color,
                                  e.target.value
                                );
                              }
                            }}
                          />
                        </TableCell>

                        <TableCell align="right">
                          <IconButton
                            onClick={(e) =>
                              handleAdd(
                                nest.id,
                                nest.prodName,
                                nest.price,
                                nest.size,
                                nest.cat,
                                nest.color
                              )
                            }
                          >
                            <AddIcon color="success" />
                          </IconButton>

                          <IconButton onClick={() => handleRemove(nest)}>
                            <RemoveIcon />
                          </IconButton>
                        </TableCell>
                        <TableCell>
                          {Number(nest.quantity) * Number(nest.price)}
                        </TableCell>
                      </TableRow>
                    ))}
                  </TableBody>
                </Table>
              </TableContainer>
            ))}
          <Pagination
            count={noOfPages}
            page={page}
            onChange={handleChange}
            defaultPage={1}
            color="secondary"
            shape="rounded"
            size="large"
            showFirstButton
            showLastButton
            style={{ margin: "0 auto" }}
          />
          -----------------------------------------------------
          <div>
            <b>Total Amount :{totalAmount}</b>
            <br />
            <b>
              Total amount but in string:{" "}
              {totalAmount.toLocaleString(navigator.language, {
                minimumFractionDigits: 2
              })}{" "}
            </b>
          </div>
          {cartItems.length >= 1 && <Button type="submit">Save Order</Button>}
        </div>
      </form>
    </div>
  );
};

export default Cart;

I have this data where it repeats the items that were placed. How can I group the data according to their id and add an array of variations that will store the various colors and their quantity ?

This is the current sample data that I have:

const data = [
  {
    id: "aRLMZkiSU7T0lcsPCSsV",
    name: "Tumbler",
    price: 200,
    size: "500",
    cat: "ML",
    color: "Green",
    quantity: 2
  },
  {
    id: "aRLMZkiSU7T0lcsPCSsV",
    name: "Tumbler",
    price: 200,
    size: "500",
    cat: "ML",
    color: "Pink",
    quantity: 1
  },
  {
    id: "aRLMZkiSU7T0lcsPCSsV",
    name: "Tumbler",
    price: 200,
    size: "500",
    cat: "ML",
    color: "Black",
    quantity: 11
  },
  {
    id: "y9ECyZBKp2OBekmWym4M",
    name: "Notebook",
    price: 250,
    size: "200",
    cat: "CM",
    color: "Red",
    quantity: 1
  },
  {
    id: "y9ECyZBKp2OBekmWym4M",
    name: "Notebook",
    price: 250,
    size: "200",
    cat: "CM",
    color: "Green",
    quantity: 4
  }
];

I just want to know how I could destructure this data to something like this where the duplicated id will be group and then the color and quantity will be pushed inside the variations array:

   id: "aRLMZkiSU7T0lcsPCSsV",
    name: "Tumbler",
    price: 200,
    size: "500",
    cat: "ML",
    variations: [
       {name: "Green", quantity: 1},
       {name: "Black", quantity: 10},
 ]

From what I have done, I have merged it already except for the part for the variations array. How can I push the variations ?

codesandbox: Data restructure - CodeSandbox

const mapById = data.reduce(
  (acc, { id, name, size, cat, price, color, quantity }) => {
    acc[id] = {
      id,
      name,
      size,
      cat,
      price,
      color,
      quantity
    };
    return acc;
  },
  {}
);