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

Next.js 13+React中Context API实现购物车数量同步问题求助

购物车数量动态同步解决方案

问题现状

使用Next.js 13 + React开发应用,包含Cart、Button、Counter组件。需求是点击Button组件时,将Button接收的quantity参数值同步显示在导航栏的Cart组件中。尝试用Context API传递值,但Cart始终显示Context默认值0;修改默认值能生效,但动态传递的quantity无法更新Cart显示,用useState也没解决。

核心问题

  1. Context作用域错误:当前Context在Button组件内创建,且Provider仅包裹Button自身,Cart组件不在该Provider的覆盖范围内,因此Cart只能读取Context的默认值,无法获取Button传递的动态值。
  2. 无全局状态管理:没有维护全局的购物车数量状态,仅传递单个Button的quantity,点击时没有更新全局累计值。

修复步骤

1. 抽离全局Cart Context

创建单独的CartContext.tsx文件,维护全局购物车数量状态及更新方法:

"use client";
import { createContext, useContext, useState, ReactNode } from "react";

type CartContextType = {
  cartQuantity: number;
  addToCart: (quantity: number) => void;
};

const CartContext = createContext<CartContextType>({
  cartQuantity: 0,
  addToCart: () => {},
});

export function CartProvider({ children }: { children: ReactNode }) {
  const [cartQuantity, setCartQuantity] = useState(0);

  const addToCart = (quantity: number) => {
    setCartQuantity(prev => prev + quantity);
  };

  return (
    <CartContext.Provider value={{ cartQuantity, addToCart }}>
      {children}
    </CartContext.Provider>
  );
}

export function useCart() {
  return useContext(CartContext);
}

2. 在根布局中包裹Provider

修改app/layout.tsx,用CartProvider包裹整个应用,确保Cart和Button都在Context作用域内:

import { CartProvider } from "./CartContext";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <CartProvider>{children}</CartProvider>
      </body>
    </html>
  );
}

3. 修改Button组件

移除内部的Context,改用全局的useCart钩子,点击时调用addToCart更新全局数量:

"use client";
import { useState, useEffect } from "react";
import { CgShoppingCart } from "react-icons/cg";
import toast, { Toaster } from "react-hot-toast";
import { useCart } from "../CartContext";

const Button = ({
  quantity,
  price,
  product_id,
}: {
  quantity: number;
  price: number;
  product_id: string;
}) => {
  const initialButtonClassName =
    "flex bg-[#212121] text-white justify-center items-center px-4 py-2 border-t-2 border-t-[#545454] border-l-2 border-l-[#545454] border-r-2 border-r-black border-b-2 border-b-black";
  const activeButtonClassName =
    "flex bg-[#212121] text-white justify-center items-center px-4 py-2 border-t-2 border-t-black border-l-2 border-l-black border-r-2 border-r-[#545454] border-b-2 border-b-[#545454]";
  const [buttonClassName, setButtonClassName] = useState(initialButtonClassName);
  const { addToCart } = useCart();

  const sendData = async () => {
    try {
      const response = await fetch("/api/cart", {
        method: "POST",
        body: JSON.stringify({
          quantity,
          price,
          product_id,
        }),
      });
      if (response.ok) {
        toast.success("Successfully added product to your cart. Enjoy");
        // 更新全局购物车数量
        addToCart(quantity);
        return await response.json();
      } else {
        toast.error("Sorry something went wrong");
        return await response.json();
      }
    } catch (error) {
      console.error("An error occurred while adding product to cart:", error);
    }
  };

  const handleClick = () => {
    setButtonClassName(activeButtonClassName);
    sendData();
  };

  useEffect(() => {
    if (buttonClassName === activeButtonClassName) {
      const timeout = setTimeout(() => {
        setButtonClassName(initialButtonClassName);
      }, 100);
      return () => clearTimeout(timeout);
    }
  }, [buttonClassName, initialButtonClassName, activeButtonClassName]);

  return (
    <button className={buttonClassName} onClick={handleClick}>
      <Toaster />
      <CgShoppingCart style={{ fontSize: "24px", color: "white" }} />
      Add to Cart
    </button>
  );
};

export default Button;

4. 修改Cart组件

改用全局的useCart钩子获取实时的购物车数量:

"use client";
import { CgShoppingCart } from "react-icons/cg";
import { useCart } from "../CartContext";

export default function Cart() {
  const { cartQuantity } = useCart();

  return (
    <div className="flex w-12 h-12 relative rounded-full items-center justify-center bg-[#F1F1F1] hover:transform hover:transition-transform hover:duration-400 hover:ease-in-out">
      <div className="flex justify-center items-center w-4 h-4 absolute rounded-full bg-red-500 text-white right-1 top-0">
        {cartQuantity}
      </div>
      <CgShoppingCart style={{ fontSize: "24px" }} />
    </div>
  );
}

说明

  • 全局Context确保Cart和Button共享同一状态,避免作用域问题。
  • addToCart方法实现数量累加,符合购物车的实际逻辑(多次添加商品数量累计)。
  • 所有客户端组件添加"use client"指令,适配Next.js 13的App Router规则。

内容的提问来源于stack exchange,提问作者no iam

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 06:00:09