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

React购物车删除商品后无法实时更新数量与总价问题

React购物车组件增删商品后页面不更新及总价异常的修复方案

问题根源

  1. 状态更新异步性导致旧数据依赖:调用setOrders后,orders状态不会立即更新,原代码直接用orders.map计算总价,拿到的是更新前的旧数据,导致总价计算错误。
  2. 全局变量total的闭包陷阱:组件顶层的let total = 0在每次渲染时都会重置,异步回调里累加的是旧闭包中的total值,最终导致总价异常归零。
  3. totalPrice初始化错误:useState(Number)把初始值设为构造函数而非数值0,初始状态不符合预期。
  4. 删除操作未等待接口完成:deleteOrder(oid)是异步操作,原代码直接调用userOrders,可能在删除接口还没执行完就获取数据,拿到的还是旧订单列表。

修复步骤

1. 修正总价初始值

将const [totalPrice, setTotalPrice] = useState(Number);改为const [totalPrice, setTotalPrice] = useState(0);,确保初始总价为有效数值。

2. 基于最新订单数据计算总价

不再依赖状态orders,直接用接口返回的最新订单数组计算总价,避开异步更新的延迟问题。

3. 等待删除接口完成后再刷新订单

在cancelOrder函数中用await等待删除接口执行完毕,再获取最新订单数据,保证拿到的是删除后的结果。

4. 提取重复逻辑减少冗余

把获取订单、计算总价的代码抽成单独函数fetchUserOrders,避免在useEffect和cancelOrder中重复写相同逻辑。

修复后的完整代码

function Basket() {
  const navigate = useNavigate();
  const [orders, setOrders] = useState<Product[]>([]);
  const [totalPrice, setTotalPrice] = useState(0);

  const session = sessionStorage.getItem("user");

  // 统一处理订单获取与总价计算
  const fetchUserOrders = async () => {
    if (!session) {
      navigate("/login");
      return;
    }
    const plainText = decrypt(session);
    const user = JSON.parse(plainText) as User;
    const res = await userOrders(user.uid);
    const newOrders = res.data.products;
    setOrders(newOrders);
    // 用reduce计算总价,简洁高效
    const total = newOrders.reduce((sum, item) => sum + item.quantity * item.price, 0);
    setTotalPrice(total);
  };

  useEffect(() => {
    fetchUserOrders();
  }, []);

  const cancelOrder = async (oid: number) => {
    // 等待删除接口完成
    await deleteOrder(oid);
    // 重新获取最新订单
    await fetchUserOrders();
  };

  return (
    <div>
      <>
        {orders.length > 0 && (
          <div className="container">
            <div className="col-sm-6">
              <table className="table">
                <thead>
                  <tr>
                    <th scope="col">Brand</th>
                    <th scope="col">Title</th>
                    <th scope="col">Price</th>
                    <th scope="col">Quantity</th>
                    <th scope="col">Delete</th>
                  </tr>
                </thead>
                <tbody>
                  {orders.map((item) => (
                    {/* 用订单ID作为key,比index更稳定 */}
                    <tr key={item.oid}>
                      <th scope="row">{item.brand}</th>
                      <td>{item.title}</td>
                      <td>{item.price}</td>
                      <td>{item.quantity}</td>
                      <td>
                        <Button
                          className="btn btn-danger btn-sm"
                          onClick={() => cancelOrder(item.oid)}
                        >
                          Delete
                        </Button>
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
            <div className="col-sm-6">
              <h4>Total Price : {totalPrice}</h4>
            </div>
          </div>
        )}
      </>
    </div>
  );
}

额外优化点

  • 用reduce替代map累加总价,代码更简洁高效。
  • 列表项的key从index改为item.oid,避免因列表顺序变化导致的不必要DOM重渲染。

内容的提问来源于stack exchange,提问作者Oğuzhan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 01:59:51