You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React简易购物车数量数据跨组件传递不一致问题求助

React购物车状态传递异常问题

我正在学习前端开发,用React写简易购物车练手。修改商品数量时,希望按商品ID生成数量值对象,传递给购物车组件展示。在ProductCard组件里,QTY状态的数量显示正常,但把这个状态通过父组件App传递给ShoppingCart组件后,用console.log查看数据会出现异常:有时完全没更新,有时数值少1。

我知道可以用useContext或Redux这类状态管理工具做全局状态,比子传父再传子方便,但目前不想用这些工具。


组件代码

App组件

import { useState } from "react";
import ProductCard from "./components/ProductCard";
import ShoppingCart from "./components/ShoppingCart";

function App() {
  let [productData, setProductData] = useState({});

  function getProductData(cartData: object) {
    setProductData(cartData);
  }

  return (
    <>
      <div>
        <ProductCard passProductData={getProductData} />
        <ShoppingCart productData={productData} />
      </div>
    </>
  );
}

export default App;

ShoppingCart组件

interface Props {
  productData: object;
}

function ShoppingCart({ productData }: Props) {
  return (
    <>
      <button
        onClick={() => {
          console.log(JSON.stringify(productData));
        }}
        className="bg-slate-400"
      >
        Test passed data
      </button>
      <h1 className="font-bold">Your cart</h1>
      <p className="font-bold">Order Total</p>
    </>
  );
}

export default ShoppingCart;

ProductCard组件

import productList from "../data/productList";
import { useState } from "react";

interface Props {
  passProductData: (quantities: object) => void;
}

function ProductCard({ passProductData }: Props) {
  let [newProductList, changeProductList] = useState(productList);
  let [productQuantities, setProductQuantities] = useState({
    0: 0,
    1: 0,
    2: 0,
    3: 0,
    4: 0,
    5: 0,
  });
  function revealQuantityInput(id: number) {
    changeProductList(
      newProductList.map((product) => {
        if (product.id === id) {
          return { ...product, quantityVisibility: true };
        } else return product;
      })
    );
  }

  function increaseQuantity(id: number) {
    for (let [key, value] of Object.entries(productQuantities)) {
      if (Number(key) === id) {
        setProductQuantities({
          ...productQuantities,
          [key]: value + 1,
        });
      }
    }
    passProductData(productQuantities);
  }

  function decreaseQuantity(id: number) {
    for (let [key, value] of Object.entries(productQuantities)) {
      if (Number(key) === id) {
        setProductQuantities({
          ...productQuantities,
          [key]: value > 0 ? value - 1 : value,
        });
      }
    }
    passProductData(productQuantities);
  }

  function setSpecificQuantityValue(id: number, quantity: number) {
    newProductList.map((product) => {
      if (product.id === id) {
        return setProductQuantities({
          ...productQuantities,
          [product.id]: quantity,
        });
      }
    });
    passProductData(productQuantities);
  }

  return (
    <>
      {newProductList.map((product) => (
        <div className="p-6 mb-6 [&>*]:mb-4" key={product.id}>
          <img src={product.image} />
          <div
            className="bg-green-600 p-2"
            onClick={() => revealQuantityInput(product.id)}
          >
            {product.quantityVisibility ? (
              <div>
                <p onClick={() => decreaseQuantity(product.id)}>-</p>
                <input
                  type="number"
                  id={"quantity" + product.id}
                  min="1"
                  max="9"
                  step="1"
                  onChange={(e) =>
                    setSpecificQuantityValue(product.id, Number(e.target.value))
                  }
                  className="bg-gray-300 w-[150px] opacity-100 mr-3"
                />
                <p onClick={() => increaseQuantity(product.id)}>+</p>
              </div>
            ) : (
              <p>Add to Cart</p>
            )}
          </div>
          <p>{product.category}</p>
          <p>{product.name}</p>
          <p>{product.price}</p>
          <p>QTY: {JSON.stringify(productQuantities)}</p>
        </div>
      ))}
    </>
  );
}

export default ProductCard;

商品数据

import productTypes from "../types/productTypes"

const productList:productTypes[] = [
    {
        id: 0,
        image: "../../images/image-waffle-mobile.jpg",
        name: "Waffle with Berries",
        category: "Waffle",
        price: 6.50,
        quantityVisibility: false,
    },
    {
        id: 1,
        image: "../../images/image-creme-brulee-mobile.jpg",
        name: "Vanilla Bean Crème Brûlée",
        category: "Crème Brûlée",
        price: 7.00,
        quantityVisibility: false,
    },
    {
        id: 2,
        image: "../../images/image-macaron-mobile.jpg",
        name: "Macaron Mix of Five",
        category: "Macaron",
        price: 8.00,
        quantityVisibility: false,
    },
    {
        id: 3,
        image: "../../images/image-tiramisu-mobile.jpg",
        name: "Classic Tiramisu",
        category: "Tiramisu",
        price: 5.50,
        quantityVisibility: false,
    },
    {
        id: 4,
        image: "../../images/image-baklava-mobile.jpg",
        name: "Pistachio Baklava",
        category: "Baklava",
        price: 4.00,
        quantityVisibility: false,
    },
    {
        id: 5,
        image: "../../images/image-meringue-mobile.jpg",
        name: "Pistachio Baklava",
        category: "Baklava",
        price: 4.00,
        quantityVisibility: false,
    },

]

export default productList

问题原因及解决方案

核心问题

React的setState是异步操作,你在调用setProductQuantities后立刻调用passProductData(productQuantities),此时传递的还是更新前的旧状态值,因为状态更新还没完成。这就导致父组件拿到的数据总是慢一步,甚至没更新。

修复方案

1. 用setState回调获取最新状态

修改increaseQuantity、decreaseQuantity、setSpecificQuantityValue这三个函数,在setProductQuantities的回调里调用passProductData,确保传递的是更新后的最新状态:

// 修改increaseQuantity
function increaseQuantity(id: number) {
  setProductQuantities(prev => {
    const newQuantities = { ...prev };
    newQuantities[id] = (newQuantities[id] || 0) + 1;
    passProductData(newQuantities);
    return newQuantities;
  });
}

// 修改decreaseQuantity
function decreaseQuantity(id: number) {
  setProductQuantities(prev => {
    const newQuantities = { ...prev };
    newQuantities[id] = Math.max(0, (newQuantities[id] || 0) - 1);
    passProductData(newQuantities);
    return newQuantities;
  });
}

// 修改setSpecificQuantityValue
function setSpecificQuantityValue(id: number, quantity: number) {
  setProductQuantities(prev => {
    const newQuantities = { ...prev };
    newQuantities[id] = Math.max(0, quantity); // 确保数量不小于0
    passProductData(newQuantities);
    return newQuantities;
  });
}

2. 简化状态更新逻辑

去掉原来的for循环遍历对象,直接通过id定位修改,代码更简洁高效。

3. 优化newProductList状态更新

revealQuantityInput里的状态更新改用回调形式,避免直接依赖外部的newProductList:

function revealQuantityInput(id: number) {
  changeProductList(prevList => 
    prevList.map(product => 
      product.id === id ? { ...product, quantityVisibility: true } : product
    )
  );
}

修复后效果

修改后,每次状态更新完成后才会把最新的数量数据传递给父组件,ShoppingCart组件拿到的就是实时的正确数据,console.log输出也会和ProductCard里的显示一致。


内容的提问来源于stack exchange,提问作者Darius Molotokas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 01:22:05