React Context State Not Consistently Updating - useStateContext Returns Null - CART NO PRODUCT

36 views Asked by At
react - nextjs - tailwind - sanity

I'm encountering an issue in my React application where the state within a Context is not consistently updating. Specifically, I'm using useStateContext to access the context's state, and after successfully updating the selected variant, useStateContext returns null. The relevant code snippets are provided below:

its live on https://reanalog-nextjs-project.vercel.app/product/boardmen after add to cart , i dont see the product in the cartpage, please anybody can help ?

(StateContext.js): import React, { createContext, useContext, useState, useEffect } from 'react'; import { toast } from 'react-hot-toast';  const Context = createContext();  export const StateContext = ({ children }) => {   console.log('StateContext wird aufgerufen');    const [showCart, setShowCart] = useState(false);   const [cartItems, setCartItems] = useState([]);   const [totalPrice, setTotalPrice] = useState(0);   const [totalQuantities, setTotalQuantities] = useState(0);   const [qty, setQty] = useState(1);   const [selectedVariant, setSelectedVariant] = useState(null);      let foundProduct;   let index;        const updateSelectedVariant = (variant) => {     setSelectedVariant(variant);   };       const onAdd = (variant, product, quantity) => {     if (!variant) {       // Handle case where variant is not defined       return;     }      setCartItems(prevItems => [...prevItems, {variant, product, quantity}]);               const checkProductInCart = cartItems.find((item) => item._id === product._id);        setTotalPrice((prevTotalPrice) => prevTotalPrice + variant.price * quantity);     setTotalQuantities((prevTotalQuantities) => prevTotalQuantities + quantity);        if (checkProductInCart) {       const updatedCartItems = cartItems.map((cartProduct) => {         if (cartProduct._id === product._id && cartProduct.variant._key === variant._key) {           return {             ...cartProduct,             quantity: cartProduct.quantity + quantity           };         }         return cartProduct;       });          setCartItems(updatedCartItems);     } else {       // Initialisiere `product` als Objekt       const newProduct = {         ...product,         quantity: quantity,         variant: variant       };          setCartItems(prevItems => [...prevItems, newProduct]);     }        toast.success(`${quantity} ${product.name} (${variant.size}) wurde dem Warenkorb hinzugefügt.`);   };      const onRemove = (product) => {     foundProduct = cartItems.find((item) => item._id === product._id);     const newCartItems = cartItems.filter((item) => item._id !== product._id);      setTotalPrice((prevTotalPrice) => prevTotalPrice -foundProduct.price * foundProduct.quantity);     setTotalQuantities(prevTotalQuantities => prevTotalQuantities - foundProduct.quantity);     setCartItems(newCartItems);   }    const toggleCartItemQuanitity = (id, value, variant) => {     foundProduct = cartItems.find((item) => item._id === id);     index = cartItems.findIndex((product) => product._id === id);     const newCartItems = cartItems.filter((item) => item._id !== id);        if (foundProduct.variant._key === selectedVariant._key) {       if (value === 'inc') {         setCartItems([...newCartItems, { ...foundProduct, quantity: foundProduct.quantity + 1 }]);         setTotalPrice((prevTotalPrice) => prevTotalPrice + foundProduct.variant.price);         setTotalQuantities((prevTotalQuantities) => prevTotalQuantities + 1);       } else if (value === 'dec') {         if (foundProduct.quantity > 1) {           setCartItems([...newCartItems, { ...foundProduct, quantity: foundProduct.quantity - 1 }]);           setTotalPrice((prevTotalPrice) => prevTotalPrice - foundProduct.variant.price);           setTotalQuantities((prevTotalQuantities) => prevTotalQuantities - 1);         }       }     }   };    const incQty = () => {     setQty((prevQty) => prevQty + 1);   }    const decQty = () => {     setQty((prevQty) => {       if(prevQty - 1 < 1) return 1;             return prevQty - 1;     });   }    return (     <Context.Provider       value={{         showCart,         setShowCart,         cartItems,         totalPrice,         totalQuantities,         qty,         incQty,         decQty,         onAdd,         toggleCartItemQuanitity,         onRemove,         setCartItems,         setTotalPrice,         setTotalQuantities,         selectedVariant,         updateSelectedVariant,                }}     >       {children}     </Context.Provider>   ) }  export const useStateContext = () => useContext(Context);  

(ProductDetails.js): [slug].js

import React, { useState } from "react";
import { Disclosure, RadioGroup, Tab } from "@headlessui/react";
import { HeartIcon } from "@heroicons/react/24/outline";
import { Product } from "../../components";
import { client, urlFor } from "../../lib/client";
import { useStateContext } from "../../context/StateContext";


const ProductDetails = ({ product, products }) => {
  const {
    image,
    name,
    details,
    price,
    beschreibung,
    edition,
    technik,
    jahr,
    variants,

  } = product;

  const { decQty, incQty, qty, onAdd, setShowCart, toggleCartItemQuanitity } = useStateContext();
  const [selectedVariant, setSelectedVariant] = useState(variants && variants.length > 0 ? variants[0] : null);

  const handleAddToCart = () => {
    onAdd(selectedVariant, product, qty);
  };

  const handleToggleCartItemQuantity = (id, value) => {
    toggleCartItemQuanitity(id, value, selectedVariant);
  };

  const handleVariantChange = (variant) => {
    setSelectedVariant(variant);
  };

  const [index, setIndex] = useState(0);

  console.log("Ausgewählte Variante:", selectedVariant);
  
  return (
    <div className="bg-grey xl:w-[1200px] mx-auto">
      <div className="mx-auto max-w-2xl px-4 py-16 sm:px-6 sm:py-24 lg:px-0 lg:max-w-7xl">
        <div className="lg:grid lg:grid-cols-2 lg:items-start lg:gap-x-8">
          {/* Image gallery */}
          <Tab.Group as="div" className="flex flex-col-reverse">
            {/* Image selector */}
            <div className="mx-auto mt-6 w-full max-w-2xl sm:block lg:max-w-none">
              <Tab.List className="grid grid-cols-4 gap-6">
                {image?.map((item, i) => (
                  <Tab
                    key={item._key}
                    className="relative flex h-24 cursor-pointer items-center justify-center 
                     bg-white text-sm font-medium uppercase text-gray-900 hover:bg-gray-50 
                     "
                  >
                    {({ selected }) => (
                      <>
                        <span className="sr-only">{image.name}</span>
                        <span className="absolute inset-0 overflow-hidden">
                          <img
                            src={urlFor(item)}
                            alt=""
                            className="h-full w-full object-cover object-center"
                          />
                        </span>
                        <span />
                      </>
                    )}
                  </Tab>
                ))}
              </Tab.List>
            </div>

            <Tab.Panels className="aspect-h-1 aspect-w-1 w-full">
              {image?.map((item, i) => (
                <Tab.Panel key={item._key}>
                  <img
                    src={urlFor(item)}
                    alt={image.alt}
                    className="h-full w-full object-cover object-center "
                  />
                </Tab.Panel>
              ))}
            </Tab.Panels>
          </Tab.Group>

          {/* Product info */}
          <div className="mt-10 px-4 sm:mt-16 sm:px-0 lg:mt-0">
            <div className="flex justify-between mt">
              <h1 className="text-3xl font-bold tracking-tight text-black">
                {name}
              </h1>
              <img src="\icons\HerzKonturreact.svg" />
            </div>
            <div className="flex">
              <div className="mt-10">
                <p>Edition</p>
                <p>Edition</p>
                <p>Edition</p>
              </div>
              <div className="mt-10 ml-7">
                <p>{edition}</p>
                <p>{technik}</p>
                <p>{jahr}</p>
              </div>
            </div>

            <div className="mt-6">
              <h3 className="sr-only">Description</h3>
              <div className="space-y-6 text-black" /> <p>{beschreibung}</p>
            </div>

            <form className="mt-6">
              Colors
              {/* <div>
                <h3 className="text-sm text-gray-600">Color</h3>

                <RadioGroup value={selectedColor} onChange={setSelectedColor} className="mt-2">
                  <RadioGroup.Label className="sr-only">Choose a color</RadioGroup.Label>
                  <span className="flex items-center space-x-3">
                    {product.colors.map((color) => (
                      <RadioGroup.Option
                        key={color.name}
                        value={color}
                        className={({ active, checked }) =>
                          classNames(
                            color.selectedColor,
                            active && checked ? 'ring ring-offset-1' : '',
                            !active && checked ? 'ring-2' : '',
                            'relative -m-0.5 flex cursor-pointer items-center justify-center rounded-full p-0.5 focus:outline-none'
                          )
                        }
                      >
                        <RadioGroup.Label as="span" className="sr-only">
                          {color.name}
                        </RadioGroup.Label>
                        <span
                          aria-hidden="true"
                          className={classNames(
                            color.bgColor,
                            'h-8 w-8 rounded-full border border-black border-opacity-10'
                          )}
                        />
                      </RadioGroup.Option>
                    ))}
                  </span>
                </RadioGroup>
              </div> */}
              <div className="sm:flex-col1 mt-10 flex">
                

                <button
                  type="button"
                  className="ml-4 flex items-center justify-center rounded-md px-3 py-3 text-black hover:bg-gray-100 hover:text-gray-500"
                >
                  <HeartIcon
                    className="h-6 w-6 flex-shrink-0"
                    aria-hidden="true"
                  />
                  <span className="sr-only">Add to favorites</span>
                </button>
              </div>
            </form>
            <div className="mt-10">
              <button
                className="relative bg-white z-0 border-2 border-black bg-transparent py-2.5 px-5 font-medium 
        uppercase text-black transition-colors hover:text-white before:absolute 
        before:left-0 before:bottom-0 before:-z-10 before:h-full before:w-full before:origin-bottom-left 
        before:scale-y-0 before:bg-black before:transition-transform before:duration-300 before:content-[''] 
        before:hover:scale-y-100 hover:z-50"
              >
                90 X 90
              </button>
            </div>
            <div></div>
            {/* Varianten-Buttons mit Preisen */}
            <div className="mt-6">
              <h3 className="text-lg font-bold mb-2">Verfügbare Varianten:</h3>
              <div className="flex space-x-4">
                {variants && variants.map((variant) => (
                  <button
                    key={variant._key}
                    onClick={() => handleVariantChange(variant)}
                    className={`px-4 py-2 border ${
                      variant._key === selectedVariant?._key
                        ? "border-blue-500 bg-blue-500 text-white"
                        : "border-gray-300 hover:border-gray-500"
                    }`}
                  >
                    <div className="flex flex-col items-center">
                      <span>{variant.size}</span>
                    </div>
                  </button>
                ))}
              </div>
            </div>

            <div className="mt-3">
              <h2 className="sr-only">Product information</h2>
              <p className="text-3xl tracking-tight text-gray-900">
              {selectedVariant && (
        <span className="mt-4">${selectedVariant.price},00 €</span>
      )}
              </p>
            </div>
            <div className="mt-10">
              <button
                className="relative bg-white z-0 border-2 border-black bg-transparent py-2.5 px-5 font-medium 
        uppercase text-black transition-colors hover:text-white before:absolute 
        before:left-0 before:bottom-0 before:-z-10 before:h-full before:w-full before:origin-bottom-left 
        before:scale-y-0 before:bg-black before:transition-transform before:duration-300 before:content-[''] 
        before:hover:scale-y-100 hover:z-50"
                onClick={handleAddToCart}
              >
                ADD TO CART
                
              </button>
            </div>
          </div>
        </div>
      </div>

      <div className="bg-white mx-auto mt-28 xl:w-[1200px]">
        <p>WEITERES AUS DIESER KOLLEKTION</p>
        <div className="mx-auto overflow-hidden py-16 sm:py-24">
          <div className="grid grid-cols-1 gap-x-6 gap-y-10 md:grid-cols-2 lg:grid-cols-3 lg:gap-x-8">
            {products?.map((product) => (
              <Product key={product._id} product={product} />
            ))}
          </div>
        </div>
      </div>
    </div>
  );
};


export const getStaticPaths = async () => {
  const query = `*[_type == "product"] {
    slug {
      current
    }
  }`;
  const products = await client.fetch(query);

  const paths = products.map((product) => ({
    params: {
      slug: product.slug.current,
    },
  }));
  return {
    paths,
    fallback: "blocking",
  };
};

export const getStaticProps = async ({ params: { slug }}) => {
  const query = `*[_type == "product" && slug.current == '${slug}'][0]`;
  const productsQuery = '*[_type == "product"]'
  
  const product = await client.fetch(query);
  const products = await client.fetch(productsQuery);

  console.log("ausgewahltes produkt", product);

  return {
    props: { products, product }
  }
}


export default ProductDetails;

CartPage cart.js

import Link from "next/link";

import React, { useRef } from "react";
import { CheckIcon, ClockIcon } from "@heroicons/react/20/solid";
import toast from "react-hot-toast";
import { useStateContext } from "../context/StateContext";
import { urlFor } from "@/lib/client";

function CartPage() {
  const cartRef = useRef();
  const { totalPrice, totalQuantities, cartItems, setShowCart, toggleCartItemQuanitity, onRemove,  updateSelectedVariant, selectedVariant } =
    useStateContext();



    const handleVariantChange = (variants) => {
        updateSelectedVariant(variants);
      };


console.log("Ausgabe von useStateContext:", selectedVariant);

    

  return (
    <div className="bg-white">
      <div className="mx-auto w-[1200px] py-16 font-primary sm:py-24 ">
        <h1 className="text-3xl font-bold tracking-tight text-gray-900">
          CART
        </h1>
          {selectedVariant && (
          <p>
            Ausgewählte Variante: {selectedVariant.size}, Preis:{" "}
            {selectedVariant.price}
          </p>
        )}
        ({totalQuantities} Artikel)
        <form className="mt-12">
          <div>
            <h2 className="sr-only">Artikel in deinem Warenkorb</h2>

            <div className="product-container">
              {cartItems.length >= 1 &&
                cartItems.map((item) => (
                  <div className="product" key={item._id}>
                    <img src={urlFor(item?.image[0])} className="cart-product-image" />
                    <div className="item-desc">
                      <div className="flex top">
                        <h5>{item.name}</h5>
                        {item.variants && <span>{item.variants.size}</span>}
                        <h4>${item.price}</h4>
                      </div>
                      <div className="flex bottom">
                        <div>
                          <p className="quantity-desc">
                            <span className="minus" onClick={() => toggleCartItemQuanitity(item._id, 'dec')}>
                              <AiOutlineMinus />
                            </span>
                            <span className="num">{item.quantity}</span>
                            <span className="plus" onClick={() => toggleCartItemQuanitity(item._id, 'inc')}><AiOutlinePlus /></span>
                          </p>
                        </div>
                        <button
                          type="button"
                          className="remove-item"
                          onClick={() => onRemove(item)}
                        >
                          <TiDeleteOutline />
                        </button>
                      </div>
                    </div>
                  </div>
                ))}
            </div>
          </div>
         
          {/* Zusammenfassung der Bestellung */}
          <div className="mt-10 w-[1200px]">
            <div className="rounded-lg bg-gray-50 px-4 py-6 sm:p-6 lg:p-8">
              <h2 className="sr-only">Zusammenfassung der Bestellung</h2>

              <div className="flow-root">
                <dl className="-my-4 divide-y divide-gray-200 text-sm">
                  <div className="flex items-center justify-between py-4">
                    <dt className="text-gray-600">Zwischensumme</dt>
                    <dd className="font-medium text-gray-900">${totalPrice.toFixed(2)}</dd>
                  </div>
                  {/* ... Weitere Details zu Versand und Steuern */}
                  <div className="flex items-center justify-between py-4">
                    <dt className="text-base font-medium text-gray-900">
                      Gesamtsumme
                    </dt>
                    <dd className="text-base font-medium text-gray-900">
                      ${totalPrice.toFixed(2)}
                    </dd>
                  </div>
                </dl>
              </div>
            </div>
            <div className="mt-10">
              <button
                type="submit"
                className="w-full rounded-md border border-transparent bg-black px-4 py-3 text-base font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 focus:ring-offset-gray-50"
              >
                Zur Kasse
              </button>
            </div>

            <div className="mt-6 text-center text-sm text-gray-500">
              <p>
                oder
                <a
                  href="#"
                  className="font-medium text-indigo-600 hover:text-indigo-500"
                >
                  Weiter Einkaufen
                  <span aria-hidden="true"> &rarr;</span>
                </a>
              </p>
            </div>
          </div>
        </form>
      </div>
    </div>
  );
}

export default CartPage;

its live on https://reanalog-nextjs-project.vercel.app/product/boardmen after add to cart , i dont see the product in the cartpage, please anybody can help ?

0

There are 0 answers