I need help
I have a state depending on another state and want to update the second state based on the first state in the code below the setTotalPrice
Doesn't get the value of ingredientsPrice pizzaPrice
when I update the ingredientsPrice
import React, { useState } from "react";
import classes from "../../styles/Pages/Product.module.scss";
import Image from "next/image";
import axios from "axios";
function Product({ pizza }) {
// The State of Pizza Size
const [size, setSize] = useState(0);
const [ingredientsPrice, setIngredientsPrice] = useState(0);
const [pizzaPrice, setPizzaPrice] = useState(pizza.price[size]);
const [totalPrice, setTotalPrice] = useState(pizza.price[size]);
const handleIngredients = async (e, ingPrice) => {
// add ingredients Price on every change and call total handler fn();
if (e.target.checked) {
setIngredientsPrice((prevIngPrice) => prevIngPrice ingPrice);
handleTotalPrice();
} else {
setIngredientsPrice((prevIngPrice) => prevIngPrice - ingPrice);
handleTotalPrice();
}
};
const handleTotalPrice = () => {
// Calc the pizza price ing price and update total
setTotalPrice(pizzaPrice ingredientsPrice);
};
return (
<div className={classes.Container}>
<div className={classes.Left}>
<div className={classes.ImgContainer}>
<Image
alt={pizza.title}
src={pizza.image}
layout="fill"
objectFit="contain"
/>
</div>
</div>
<div className={classes.Right}>
<h1 className={classes.Title}>{pizza.title}</h1>
<span className={classes.Price}>${totalPrice}</span>
<p className={classes.Description}>{pizza.description}</p>
<h3 className={classes.Choose}>Choose Your Size</h3>
<div className={classes.Sizes}>
<div
className={classes.SizeItem}
onClick={() => setSize(0)}
>
<Image
src={"/Images/size.png"}
alt="Small Size"
layout="fill"
/>
<span className={classes.Size}>Small</span>
</div>
<div
className={classes.SizeItem}
onClick={() => setSize(1)}
>
<Image
src={"/Images/size.png"}
alt="Small Size"
layout="fill"
/>
<span className={classes.Size}>Medium</span>
</div>
<div
className={classes.SizeItem}
onClick={() => setSize(2)}
>
<Image
src={"/Images/size.png"}
alt="Small Size"
layout="fill"
/>
<span className={classes.Size}>Large</span>
</div>
</div>
<h3 className={classes.Choose}>
Choose Additional Ingredients
</h3>
<div className={classes.Ingredients}>
{pizza.extraOptions.map((cur, index) => {
const trimedName = cur.extra.trim();
const ingPrice = cur.price;
return (
<div
className={classes.IngredientOption}
key={"Extra" index}
>
<input
type={"checkbox"}
name={trimedName}
id={trimedName}
className={classes.Checkbox}
onChange={(e) =>
handleIngredients(e, ingPrice)
}
/>
<label htmlFor={trimedName}>{cur.extra}</label>
</div>
);
})}
</div>
<div className={classes.Quentity}>
<input type={"number"} defaultValue={1} max={5} min={1} />
<button>Add to Cart</button>
</div>
</div>
</div>
);
}
export default Product;
export async function getServerSideProps({ params }) {
const pizza = await axios.get(
"http://localhost:3000/api/products/" params.id
);
return {
props: { pizza: pizza.data },
};
}
I expect the totalPrice
will update automatically when ingredientsPrice
updates
CodePudding user response:
The problem is with this:
setIngredientsPrice((prevIngPrice) => prevIngPrice - ingPrice);
handleTotalPrice();
setIngredientsPrice
actually changes the ingredientsPrice
in an async manner, so it is surely possible that the code inside handleTotalPrice
execute when ingredientsPrice
is not yet updated thus resulting with the wrong totalPrice
value.
To overcome this common problem in React, you need to move your handleTotalPrice
function call to a useEffect
hook, like this:
//update totalPrice when ingredientsPrice or pizzaPrice change
useEffect(() => {
setTotalPrice(ingredientsPrice pizzaPrice);
//or handleTotalPrice();
},[ingredientsPrice, pizzaPrice]);
The function that we pass in useEffect
hook will run whenever any of the variables listed inside dependency array (the array which we also pass into the useEffect
) change.
CodePudding user response:
State updates in React are asynchronous, that means they are not applied immediately but queued.
I would suggest that you put dependent state in one single object which you then update. ingredientsPrice
and totalPrice
are dependent. If you update one, you need to update the other as well (at least from my understanding), so put them together in an object state and update just that one state.
Be aware that you must provide a new object when you update state as only a no deep comparism is performed.
Have a look at this blog post for more details.