Home > Enterprise >  State Doesn't calculate the new value : (
State Doesn't calculate the new value : (

Time:03-23

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.

  • Related