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]);
