Home > Software design >  How to update total price of products on React functional components?
How to update total price of products on React functional components?

Time:01-10

I'm creating a e-commerce website. But i'm unable to update the total price using the useState hook of react.

When is use forEach to update total price it give me this error: Uncaught (in promise) Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.

codes:

import { Container, Grid, Typography } from "@mui/material";
import React, { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import useAuth from "../../../hooks/useAuth";
import Cart from "../Cart/Cart";
import CartProduct from "../CartProduct/CartProduct";

const CartContainer = () => {
  const { user } = useAuth();
  // useNavigate hook
  const navigate = useNavigate();
  /* states */
  const [products, setProducts] = useState([]);
  const [quantity, setQuantity] = useState(0);
  const [isDeleted, setIsDeleted] = useState(false);
  const [total, setTotal] = useState(0);

  const handleAddQuantity = () => {
    setQuantity(quantity   1);
  };
  const handleRemoveQuantity = () => {
    if (quantity === 0) {
      return;
    } else {
      setQuantity(quantity - 1);
    }
  };

  const handleDelete = (id) => {
    fetch(`https://limitless-crag-38673.herokuapp.com/cart?id=${id}`, {
      method: "DELETE",
    })
      .then((res) => res.json())
      .then((data) => {
        data.deletedCount > 0 && setIsDeleted(true);
        alert("successfully deleted");
      });
  };
  useEffect(() => {
    setIsDeleted(false);
    fetch(
      `https://limitless-crag-38673.herokuapp.com/cart/product?email=${user.email}`
    )
      .then((res) => res.json())
      .then((data) => setProducts(data));
  }, [isDeleted]);
  return (
    <>
      {/* main cart */}
      <Container sx={{ my: 3 }} maxWidth="xl">
        <Grid container spacing={{ xs: 1, md: 2 }}>
          {/* products */}
          <Grid item md={8} xs={12}>
            {/* cart title */}
            <Grid
              container
              sx={{
                borderBottom: "1px solid gray",
                display: { xs: "none", sm: "flex" },
              }}
            >
              <Grid item md={6}>
                <Typography variant="h5">Product</Typography>
              </Grid>
              <Grid item md={4}>
                <Typography variant="h5" sx={{ textAlign: "center" }}>
                  Price
                </Typography>
              </Grid>
              {/* <Grid item md={2}>
                <Typography variant="h5" sx={{ textAlign: "center" }}>
                  Quantity
                </Typography>
              </Grid>
              <Grid item md={2}>
                <Typography variant="h5" sx={{ textAlign: "center" }}>
                  Subtotal
                </Typography>
              </Grid> */}
              <Grid item md={2}>
                <Typography variant="h5" sx={{ textAlign: "center" }}>
                  Cancel
                </Typography>
              </Grid>
            </Grid>
            {/*============
           product
           ============= */}

            {products.map((product) => (
              <CartProduct
                key={product._id}
                product={product}
                handleDelete={handleDelete}
                total={total}
                setTotal={setTotal}
              />
            ))}
          </Grid>

          {/*=========== 
          cart
          ================ */}
          <Cart />
        </Grid>
      </Container>
    </>
  );
};

export default CartContainer;

`And when I try this it gives me this error: Uncaught (in promise) Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.

codes:

import CancelOutlinedIcon from "@mui/icons-material/CancelOutlined";
import { Grid, IconButton, Typography } from "@mui/material";
import { Box } from "@mui/system";
import React from "react";

const CartProduct = ({ product, handleDelete, setTotal, total }) => {
  const { name, price, src } = product;
  setTotal(total   price);

  return (
    <Grid
      container
      sx={{
        borderBottom: "1px solid gray",
        display: "flex",
        flexDirection: { xs: "column", md: "row" },
        alignItems: "center",
      }}
      spacing={{ md: 2, xs: 1 }}
    >
      {/* product */}
      <Grid
        sx={{
          display: "flex",
          alignItems: "center",
          flexDirection: { md: "row", xs: "column" },
        }}
        item
        md={6}
        xs={12}
      >
        <Box>
          <img src={src} alt="" />
        </Box>
        <Typography sx={{ ml: 1 }} variant="h6">
          {name}
        </Typography>
      </Grid>
      {/* Price */}
      <Grid
        item
        md={4}
        sx={{
          alignItems: "center",
          justifyContent: "center",
          display: "flex",
        }}
      >
        <Typography variant="h5">$ {price}</Typography>
      </Grid>
      {/* 
                //  quantity
                <Grid
                  sx={{
                    display: "flex",
                    alignItems: "center",
                    justifyContent: "flex-start",
                  }}
                  item
                  md={2}
                  xs={12}
                >
                  <IconButton onClick={handleRemoveQuantity}>
                    <RemoveCircleOutlineOutlinedIcon />
                  </IconButton>
                  <TextField value={quantity} />
                  <IconButton onClick={handleAddQuantity}>
                    <AddCircleOutlineOutlinedIcon />
                  </IconButton>
                </Grid>
                // Subtotal
                <Grid
                  item
                  md={2}
                  xs={12}
                  sx={{
                    display: "flex",
                    alignItems: "center",
                    justifyContent: "center",
                  }}
                >
                  <Typography variant="h5">$ {product.price}</Typography>
                </Grid> */}
      {/* Cancel */}

      <Grid
        item
        md={2}
        sx={{
          display: "flex",
          alignItems: "center",
          justifyContent: "center",
        }}
      >
        <IconButton onClick={() => handleDelete(product._id)}>
          <CancelOutlinedIcon fontSize="large" />
        </IconButton>
      </Grid>
    </Grid>
  );
};

export default CartProduct;

CodePudding user response:

Why not calculate Total in parent component? because setState automatically triggers re-render and in your case every rerender you trigger another setState.

if you want to store data somewhere without re-render use useRef. But in your case i would suggest to set Total price in CartContainer

CodePudding user response:

You don't need total and subTotal, you can directly calculate it from product.

The below code need to be in CartProduct as you want to apply delete on individual items.

const [isDeleted, setIsDeleted] = useState(false);

   useEffect(() => {
    setIsDeleted(false);
    fetch(
      `https://limitless-crag-38673.herokuapp.com/cart/product?email=${user.email}`
    )
      .then((res) => res.json())
      .then((data) => setProducts(data));
  }, [isDeleted]);
  •  Tags:  
  • Related