skip to Main Content

I’m encountering the error "useCart must be used within a CartContextProvider" in my NextJS application. I’m using the useCart hook from a custom useCart.tsx file and providing a CartContextProvider at the top level of my application. However, the error persists.

Are there any potential issues with my useCart hook implementation or how I’m accessing the context?

Here’s the files of my ProductDetails component where I’m using useCart:

ProductDetails:

"use client";

import Button from "@/app/components/Button";
import ProductImage from "@/app/components/products/ProductImage";
import SetColor from "@/app/components/products/SetColor";
import SetQuantity from "@/app/components/products/SetQuantity";
import { useCart } from "@/hooks/useCart";
import FormatPrice from "@/utils/formatPrice";
import { Rating } from "@mui/material";
import { useCallback, useEffect, useState } from "react";

interface ProductDetailsProps {
  data: any;
}

export type CartProductType = {
  id: string;
  name: string;
  price: number;
  description: string;
  category: string;
  brand: string;
  SelectedImg: any;
  quantity: number;
};

export type SelectedImgType = {
  color: string;
  colorCode: string;
  image: string;
};

const HorizontalLine = () => <hr className="w-[30%] my-2" />;

const ProductDetails: React.FC<ProductDetailsProps> = ({ data }) => {
  const { handleAddProductToCart, cartProducts } = useCart();
  const [isProductInCart, setIsProductInCart] = useState(false);
  const [CartProduct, setCartProduct] = useState<CartProductType>({
    id: data.id,
    name: data.name,
    price: data.price,
    description: data.description,
    category: data.category,
    brand: data.brand,
    SelectedImg: { ...data.images[0] },
    quantity: 1,
  });
  
  console.log(CartProduct);

  useEffect(() => {
    setIsProductInCart(false);

    if (cartProducts) {
      const existingIndex = cartProducts.findIndex(
        (item) => item.id === CartProduct.id
      );

      if (existingIndex !== -1) {
        setIsProductInCart(true);
      }
    }
  }, [cartProducts]);

  const productRating =
    data.reviews.reduce((acc: number, item: any) => acc + item.rating, 0) /
    data.reviews.length;

  const handleQtyIncrease = useCallback(() => {
    if (CartProduct.quantity === 99) return;

    setCartProduct((prev) => {
      return { ...prev, quantity: prev.quantity + 1};
    });
  }, [CartProduct]);

  const handleQtyDecrease = useCallback(() => {
    if (CartProduct.quantity === 1) return;

    setCartProduct((prev) => {
      return { ...prev, quantity: prev.quantity - 1};
    });
  }, [CartProduct]);

  const handleColorSelect = useCallback(
    (value: SelectedImgType) => {
      setCartProduct((prev) => {
        return { ...prev, SelectedImg: value };
      });
    },
    [CartProduct.SelectedImg]
  );

  return (
    <div className="grid grid-cols-1 md:grid-cols-2 gap-12">
      <ProductImage
        cartProduct={CartProduct}
        product={data}
        handleColorSelect={handleColorSelect}
      />
      <div className="flex flex-col gap-1 text-slate-500 text-sm">
        <h2 className="text-3xl font-medium text-slate-700">{data.name}</h2>
        <div className="flex items-center gap-2">
          <Rating value={productRating} readOnly />
          <div>{data.reviews.length} reviews</div>
        </div>
        <div className="font-bold text-2xl text-black">
          {FormatPrice(data.price)}
        </div>
        <HorizontalLine />
        <div className="text-justify">{data.description}</div>
        <HorizontalLine />
        <div>
          <span className="font-semibold">CATEGORY: </span>
          {data.category}
        </div>
        <div>
          <span className="font-semibold">BRAND: </span>
          {data.brand}
        </div>
        <div className={data.inStock ? "text-teal-400" : "text-rose-400"}>
          {data.inStock ? "In Stock" : "Out of Stock"}
        </div>
        <HorizontalLine />
        {isProductInCart ? (
          <></>
        ) : (
          <>
            {" "}
            <SetColor
              cartProduct={CartProduct}
              images={data.images}
              handleColorSelect={handleColorSelect}
            />
            <HorizontalLine />
            <SetQuantity
              cartProduct={CartProduct}
              handleQtyDecrease={handleQtyDecrease}
              handleQtyIncrease={handleQtyIncrease}
            />
            <HorizontalLine />
            <div className="max-w-[300px]">
              <Button
                label="Add To Cart"
                onClick={() => handleAddProductToCart(CartProduct)}
              />
            </div>
          </>
        )}
      </div>
    </div>
  );
};

export default ProductDetails;

useCart:

import { CartProductType } from "@/app/product/[productId]/ProductDetails";
import { createContext, useCallback, useContext, useState } from "react";

type CartContextType = {
  cartTotalQty: number;
  cartProducts: CartProductType[] | null;
  handleAddProductToCart: (product: CartProductType) => void;
};

export const CartContext = createContext<CartContextType | null>(null);

interface CartContextProviderProps {
    [probName: string]: any;
}

export const CartContextProvider = (props: CartContextProviderProps) => {

    const [cartTotalQty, setCartTotalQty] = useState(10)
    const [cartProducts, setCartProducts] = useState<CartProductType[] | null>(null);

    const handleAddProductToCart = useCallback((product: CartProductType) => {
        setCartProducts((prev) => {
            let updatedCart;

            if (prev) {
                updatedCart = [...prev, product];
            }
            else {
                updatedCart = [product];
            }

            return updatedCart;
        });
    }, []);

    const value = {
        cartTotalQty,
        cartProducts,
        handleAddProductToCart,
    };

    return <CartContext.Provider value={value} {...props}/>;
};

export const useCart = () => {
    const context = useContext(CartContext);

    if (context === null) {
        throw new Error("useCart must be used within a CartContextProvider");
    }

    return context;
}

Here is the error

I tried to ask chatgpt, but failed to find the issue

2

Answers


  1. Chosen as BEST ANSWER

    I just forgot to add the provider and missed it from the tutorial. Thank you!


  2. You need to wrap ProductDetails with CartContext

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search