State 不计算新值 :(

State Doesn't calculate the new value : (

我需要帮助

我有一个状态依赖于另一个状态,我想根据 setTotalPrice 下面代码中的第一个状态更新第二个状态 我更新时没有得到 ingredientsPrice + pizzaPrice 的值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 },
    };
}

我希望 totalPrice 会在 ingredientsPrice 更新时自动更新

React 中的状态更新是异步的,这意味着它们不会立即应用而是排队。

我建议您将依赖状态放在一个对象中,然后再对其进行更新。 ingredientsPricetotalPrice 是相关的。如果你更新一个,你也需要更新另一个(至少根据我的理解),所以将它们放在一个对象状态中并只更新那个状态。

请注意,更新状态时必须提供新对象,因为只执行非深度比较。

查看此blog post了解更多详情。

问题在于:

setIngredientsPrice((prevIngPrice) => prevIngPrice - ingPrice);
handleTotalPrice();

setIngredientsPrice 实际上以异步方式更改了 ingredientsPrice,因此肯定有可能 handleTotalPrice 中的代码在 ingredientsPrice 尚未更新时执行,从而导致totalPrice 值错误。

要克服 React 中的这个常见问题,您需要将 handleTotalPrice 函数调用移动到 useEffect 挂钩,如下所示:

    //update totalPrice when ingredientsPrice or pizzaPrice change
    useEffect(() => {
      setTotalPrice(ingredientsPrice + pizzaPrice);
      //or handleTotalPrice();
    },[ingredientsPrice, pizzaPrice]);

我们传入 useEffect 钩子的函数将 运行 每当依赖数组(我们也传入 useEffect 的数组)中列出的任何变量发生变化时。