如何在我的 react/nextjs 应用程序的每个页面上显示我的购物车中的商品数量

How can i display the quantity of the items in my shopping cart on every pages of my react/nextjs application

我的 nextjs 电子商务应用程序遇到这个特殊问题已经一个多星期了,它对我来说似乎无法解决,我必须在本周之前提交这个项目,因为它是我从我目前正在参加的 web dev BootCamp。

我有一个购物车,其中添加了所有产品的信息,在我的 header 上,我有一个购物车徽标,应该(需要)显示我购物中的商品数量应用程序每个页面上的购物车。我可以获得要显示的数量,但只有当我查看购物车时,一旦我点击离开购物车页面,数量将是不确定的。在互联网上进行严格搜索后,我了解了反应中的提升状态,我已尽我所能从他们的共同 parents 传递这些信息,在我的例子中是 _app.js 和同样的问题仍然存在。我阅读了有关反应上下文的内容,但 BootCamp 不允许我们使用反应上下文来执行此操作,这让我完全陷入了困境。请我联系任何可以帮助我的人,我不知道我是否完全弄错了,或者我是否只是不理解这个反应道具概念或我在这方面做错的特定事情。

我在这里分享了一张带有购物车和我的文件结构的截图。代码已经太多了,无法复制和粘贴到这里,所以我在代码沙箱上分享了完整的应用程序 link,任何人都可以在那里查看完整的代码。请给我任何帮助,我将不胜感激。提前致谢。

这里是Codesandbox demo

如果我需要提供更多信息,请尽管询问。非常感谢您的帮助。

这是我的购物车组件中的代码,我应该在其中更新 cartQuantity 状态变量。


import { css } from '@emotion/react';
import Head from 'next/head';
import Link from 'next/link';
// import image from 'next/image';
import { useEffect, useState } from 'react';
// import Image from 'next/image';
import Layout from '../../../components/Layout.js';
import { getParsedCookie, setParsedCookie } from '../../../util/cookies';
import { calculateTotalPrice } from '../../../util/priceChecker';

const cartStyles = css`
  max-width: 100vw;
  min-height: 100vh;
  background: #f2f2ff;
  padding-bottom: 1rem;

  .heading {
    text-align: center;
  }

  .topInfoWrap {
    width: 60%;
    margin: 0 auto;
    display: flex;
    justify-content: space-between;
    align-items: center;
    a {
      font-size: 1rem;
      border: none;
      cursor: pointer;
      background: transparent;
      text-decoration: none;
    }
    div:last-of-type {
      display: flex;
      justify-content: space-around;
      gap: 5px;
      button {
        font-size: 1.1rem;
        padding: 0 1rem;
        background: #191959;
        color: white;
      }

      button:last-of-type {
        background: #353434;
      }
    }
  }

  .cartDisplayWrapper {
    width: 70%;
    margin: 0 auto;
    background: #e1d1f5;
    border-radius: 10px;
    padding: 0.5rem;

    .itemsCount {
      margin-left: 0.5rem;
    }

    .tableHeaders {
      background: #e1d1f5;
      display: flex;
      justify-content: space-between;
      align-items: center;
      padding: 0 0.5rem;
      border-top: 2px solid black;
      border-bottom: 2px solid black;
      margin: 0;

      h2:nth-child(2) {
        width: 250px;
        text-align: right;
      }
    }

    .tableContentWrapper {
      width: 100%;
      ul {
        width: 100%;
        padding: 0;

        li {
          list-style: none;

          .tableDisplayFlex {
            width: 100%;
            padding: 0 0.5rem;
            display: flex;
            justify-content: space-between;
            align-items: center;
            background: #e1d1f5;
            border-bottom: 2px solid black;

            .itemsBox {
              display: flex;
              justify-content: space-around;
              align-items: flex-start;
              padding: 0;
              max-width: 300px;
              gap: 5px;

              img {
                width: 60px;
              }
            }

            .colorBox {
              margin-left: 1rem;
            }
            .quantityBox {
              width: 110px;
              height: 40px;
              background: #fefefe;
              display: flex;
              justify-content: space-evenly;
              align-items: center;
              font-size: 1.2rem;
              border-radius: 10rem;
              margin-right: -1.5rem;

              p {
                font-weight: bolder;
                width: 30px;
                text-align: center;
              }

              .deleteFromCart {
                background: #fb2e86;
              }

              button {
                font-size: 1.2rem;
                display: flex;
                justify-content: center;
                align-items: center;
                cursor: pointer;
                flex-basis: 35px;
                height: 35px;
                background: #353434;
                color: white;
                border: none;
                border-radius: 50%;

                span {
                  font-weight: bolder;
                  display: flex;
                  justify-content: center;
                  align-items: center;
                  width: 100%;
                  height: 100%;
                  display: inline;
                  border-radius: inherit;
                }
              }
            }

            .priceBox {
              background: #353434;
              color: white;
              width: 110px;
              display: inline-flex;
              justify-content: center;
              align-items: center;
              padding: 0;
              border-radius: 8px;
            }
          }
        }
      }
    }
  }

  .totalPriceDisplayBox {
    padding: 0 0.5rem;
    width: 100%auto;
    .itemsPrice,
    .taxPrice,
    .shippingPrice,
    .totalPrice {
      width: 100%;
      display: flex;
      justify-content: space-between;
      align-items: center;
      p {
        font-size: 1.2rem;
      }

      strong {
        font-size: 1.2rem;
      }
    }

    .totalPrice strong {
      font-size: 1.5rem;
      height: 60px;
      display: flex;
      justify-content: center;
      align-items: center;
      padding: 0 0.5rem;
      background: #353434;
      color: white;
      border-radius: 8px;
    }

    .totalPrice {
      font-weight: bold;
    }
  }
`;

export default function Cart(props) {
  console.log('props from the cart page: ', props);
  // getting all the cookie objects back from the browser
  const shoppingCartCookies = getParsedCookie('cartInside') || [];

  // setting state variables for all prices related codes!

  const [productsPrice, setProductsPrice] = useState(0);

  // setting the quantities

  // i must check this code and how it works is is still equals zero when i log it in
  const [itemQuantity, setItemQuantity] = useState(0);
  // const [cartInside, setCartInside] = useState(
  //   getParsedCookie('cartInside') || [],
  // console.log('This is the item Q State', itemQuantity);
  // );

  // const [shoppingCartQuantity, setShoppingCartQuantity] = useState(0);

  // finding the product id that matches the cookie object id that i fetched from the browser
  const foundProductsWithCookie = shoppingCartCookies.map(
    (individualCookieObj) => {
      const itemAndCookieMatched = props.products.find((product) => {
        return Number(product.id) === individualCookieObj.id;
      });

      return itemAndCookieMatched;
    },
  );

  // All the items in the cart
  console.log('ITEMS AMOUNT IN CART');
  console.log(foundProductsWithCookie);

  useEffect(() => {
    props.setCartQuantity(foundProductsWithCookie.length);
    setProductsPrice(calculateTotalPrice(foundProductsWithCookie));
  }, [foundProductsWithCookie]);

  // calculating the tax Price and shipping price and then add all together as the total price.
  const taxPrice = productsPrice * 0.13;
  const shippingPrice = productsPrice > 2000 ? 0 : 50;
  const totalPrice = Number(productsPrice) + (taxPrice + shippingPrice);

  function makeQuantityIncrement(singleProductObj) {
    // getting the current quantity back from the cookie
    const currentCookieQuantity = getParsedCookie('cartInside') || [];

    // the found product with cookie here is an array of all the product objects that is added to the cart
    currentCookieQuantity.find((singleCookieObj) => {
      // i looped over it to find a match for the product in the cart and the corresponding cookie.
      if (Number(singleProductObj.id) === singleCookieObj.id) {
        // i didn't have to return anything so i just used the value true or false to increase the
        // quantity of the item in the cart when true and nothing when not.
        console.log('New value should start here');
        const newQuantityValue = (singleProductObj.quantity += 1);

        // making the cookie quantity the same as the quantity of the items in the cart
        singleCookieObj.quantityCount = newQuantityValue;

        setItemQuantity(newQuantityValue);
      }
    });

    console.log('Checking new cookie Value');
    console.log(currentCookieQuantity);

    // setting the new cookie quantity to reflect in the browser
    setParsedCookie('cartInside', currentCookieQuantity);
  }

  // function that decreases the quantity

  function makeQuantityDecrement(singleProductObj) {
    // getting the current quantity back from the cookie
    const currentCookieQuantity = getParsedCookie('cartInside') || [];

    // the found product with cookie here is an array of all the product objects that is added to the cart
    currentCookieQuantity.find((singleCookieObj) => {
      if (Number(singleProductObj.id) === singleCookieObj.id) {
        console.log('New value should start here');
        const newQuantityValue = (singleProductObj.quantity -= 1);

        // making the cookie quantity the same as the quantity of the items in the cart
        singleCookieObj.quantityCount = newQuantityValue;

        setItemQuantity(newQuantityValue);
      }
    });

    console.log('Checking new cookie Value');
    console.log(currentCookieQuantity);

    // setting the new cookie quantity to reflect in the browser
    setParsedCookie('cartInside', currentCookieQuantity);
  }

  // Function that handles decrements limit
  function stopDecrement() {
    console.log('can not be lower than one');
  }

  // #############################
  // function for deleting item from cart

  function itemDeletionHandler(singleProductObj) {
    const currentCookie = getParsedCookie('cartInside') || [];

    const isItemInCart = currentCookie.some((cookieObj) => {
      return cookieObj.id === Number(singleProductObj.id);
    });

    let newCookies;

    if (isItemInCart) {
      newCookies = currentCookie.filter((cookieObj) => {
        return cookieObj.id !== Number(singleProductObj.id);
      });

      setParsedCookie('cartInside', newCookies);
    }
  }
  // #######################################

  return (
    <Layout
      catQuantity={props.catQuantity}
      setCartQuantity={props.setCartQuantity}
    >
      {' '}
      {/* Check please,, trying to pass props through the layout component */}
      <section css={cartStyles}>
        <Head>
          <title>Cart Section</title>
          <meta
            name="description"
            content="The Best Next eCommerce shop around here"
          />
        </Head>

        <h1 className="heading">SHOPPING CART</h1>

        <div className="topInfoWrap">
          <div>
            <Link href="/products">
              <a className="backToShopping">BACK TO SHOPPING</a>
              {/* Check here for duplicate */}
            </Link>
          </div>
          <div>
            <button>PayPal Checkout</button>
            <p>OR</p>
            <button>PROCEED WITH YOUR ORDER</button>
          </div>
        </div>

        <div className="cartDisplayWrapper">
          <div>
            <h3 className="itemsCount">
              {foundProductsWithCookie.length !== 0
                ? `ITEMS ADDED TO YOUR SHOPPING CART (${foundProductsWithCookie.length})`
                : `Your Cart is Empty`}
            </h3>
          </div>

          <div className="tableHeaders">
            <h2>ITEMS</h2>
            <h2>COLOR</h2>
            <h2>Quantity</h2>
            <h2>PRICE</h2>
          </div>

          {/* Return a table for the cart items and prices and quantity and others */}

          <div className="tableContentWrapper">
            <ul>
              {foundProductsWithCookie.map((itemWithCookie) => {
                return (
                  <li key={`item-li- ${itemWithCookie.id}`}>
                    <div className="tableDisplayFlex">
                      {/* First row */}

                      <div className="itemsBox">
                        <div>
                          {/*  <image
                            src={`/images/public/${itemWithCookie.id}.jpg`}
                            alt={itemWithCookie.title}
                            width={400}
                            height={500}
                          /> */}
                          <img
                            src={itemWithCookie.image}
                            alt={itemWithCookie.title}
                          />
                        </div>
                        <div>
                          <h3>{itemWithCookie.name}</h3>
                          <p>{itemWithCookie.title}</p>
                        </div>
                      </div>

                      {/* second row */}
                      <div className="colorBox">
                        <p>COLOR COMES HERE</p>
                      </div>
                      {/* Third row */}
                      <div className="quantityBox">
                        {itemWithCookie.quantity <= 1 ? (
                          <button
                            className="deleteFromCart"
                            value={itemWithCookie.id}
                            onClick={(event) => {
                              console.log(
                                'clicked' + event.currentTarget.value,
                              );
                              itemDeletionHandler(itemWithCookie);
                            }}
                          >
                            <span>&#215;</span>
                          </button>
                        ) : (
                          <button
                            value={itemWithCookie.id}
                            onClick={(event) => {
                              console.log(
                                'clicked' + event.currentTarget.value,
                              );
                              itemWithCookie.quantity > 1
                                ? makeQuantityDecrement(itemWithCookie)
                                : stopDecrement();
                            }}
                          >
                            <span>&#8722;</span>
                          </button>
                        )}

                        <p>{itemWithCookie.quantity}</p>

                        <button
                          value={itemWithCookie.id}
                          onClick={(event) => {
                            console.log('clicked' + event.currentTarget.value);
                            makeQuantityIncrement(itemWithCookie);
                          }}
                        >
                          <span>&#43;</span>
                        </button>
                      </div>

                      {/* Fourth Row */}
                      <div className="priceBox">
                        <h2>{`€ ${itemWithCookie.price}`}</h2>
                      </div>
                    </div>
                  </li>
                );
              })}

              <div className="totalPriceDisplayBox">
                <div className="itemsPrice">
                  <p>Items Price</p>
                  <strong>{`€ ${productsPrice}`}</strong>
                </div>

                <div className="taxPrice">
                  <p>Tax Price</p>
                  <strong>{`€ ${taxPrice.toFixed(2)}`}</strong>
                </div>

                <div className="shippingPrice">
                  <p>Shipping Price</p>
                  <strong>{`€ ${shippingPrice.toFixed(2)}`}</strong>
                </div>

                <div className="totalPrice">
                  <p>Total Price</p>
                  <strong>{`€ ${totalPrice.toFixed(2)}`}</strong>
                </div>
              </div>
            </ul>
          </div>
        </div>
      </section>
    </Layout>
  );
}

// Server side code via getServerSideProps

export async function getServerSideProps(context) {
  // getting the products from the dataBase
  const { DUUMMY_PRODUCTS } = await import('../../../util/database');

  // i get information back from the cookie in the browser which should be the cookies that the user
  // has created as he or she clicked the add to cart button which means that the information contained in this cookies
  // should have a matching product from the database
  const cookies = context.req.cookies.cartInside || '[]';
  const cartInside = JSON.parse(cookies);

  // mapping through the products array and getting the match between the information from the cookies and the matching products.
  const itemInsideCart = DUUMMY_PRODUCTS.map((product) => {
    const isTheItemInCart = cartInside.some((productCookieObj) => {
      return Number(product.id) === productCookieObj.id;
    });

    const userObj = cartInside.find((cookieOBJ) => {
      return cookieOBJ.id === Number(product.id);
    });

    if (isTheItemInCart) {
      return {
        ...product,
        cartInside: isTheItemInCart,

        // if the item is in the cart then the quantity i got back from the cookie should be added to it, if not, it should be null.
        quantity: isTheItemInCart ? userObj.quantityCount : null,
      };
    } else {
      return ''; /* Make sure this is working the way it should */
    }
  });

  return {
    props: {
      products: itemInsideCart,
      // products: DUUMMY_PRODUCTS || null
    },
  };
}

我在购物车组件中有 cartQuantity 状态变量及其 setter,但在阅读了有关提升状态的信息后,我将它们移到了 _app.js 组件,然后我通过 props 将它们传递回购物车组件,但它只在我查看购物车组件时显示购物车中的值。我尝试的一切都没有改变任何东西。一旦我离开购物车组件,该值就会变为 undefined.

下面是我的 _app.js 的代码,我在其中传递了状态变量 props 来自:

import { css, Global } from '@emotion/react';
import Head from 'next/head';
import { useState } from 'react';

function MyApp({ Component, pageProps }) {
  const [shoppingCartQuantity, setShoppingCartQuantity] = useState();
  console.log('props from app.js: ', pageProps);
  return (
    <>
      <Global
        styles={css`
          html,
          body {
            margin: 0;
            padding: 0;
            *,
            *:before,
            *:after {
              box-sizing: border-box;
              font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto,
                Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue,
                sans-serif, Josefin Sans, Lato;
            }
          }
        `}
      />
      <Head>
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <Component
        catQuantity={shoppingCartQuantity}
        setCartQuantity={setShoppingCartQuantity}
        {...pageProps}
      />
      <h1>{shoppingCartQuantity}</h1>;
    </>
  );
}

export default MyApp;


所有电子商务网站都将购物车商品存储到本地存储中,这就是为什么每次访问他们的网站时,您都可以看到购物车中已有的商品。最好的方法是将购物车项目存储在本地存储中。由于购物车图标将位于页眉内,并且页眉将显示在每个页面上,因此在 useEffect 中读取本地存储数据。

首先,您需要将项目写入本地存储。在您的 addToCart 函数(无论您如何命名)中,当您将商品添加到购物车时,将它们写入本地存储。 (如果你使用的是 redux,最好的地方是在 addToCart 操作中)

因为您正在使用 next.js

if (typeof window !== undefined) {
      localStorage.setItem(
        "cartItems",
        
        JSON.stringify(getState().cartItems)
      );
    }

我假设你没有使用 redux,所以在 useEffect 中的 Header 组件中最好的方法是:

useEffect(()=>{
     // since we are in useEffect, that means we are in browser and window is already defined
     // So you do not need to use  (typeof window !== "undefined") in useEfect
     // in case you want to use somewhere else, keep in mind you have to use (typeof window !== "undefined")
    // you do not just store cartitems in localstorage, you c an store all nonsensitive data in localstorage, thats why I put others too
    // go any ecommerce website, add something to cart, and in browser dev tools, application tab, check localstorage. you see bunch of stored data
     if (typeof window !== "undefined") {
  shippingAddressFromStorage = localStorage.getItem("shippingAddress")
    ? JSON.parse(localStorage.getItem("shippingAddress"))
    : null;
  paymentMethodFromStorage = localStorage.getItem("paymentMethod")
    ? JSON.parse(localStorage.getItem("paymentMethod"))
    : null;
  cartItemsFromStorage = localStorage.getItem("cartItems")
    ? JSON.parse(localStorage.getItem("cartItems") )
    : [];
}

      })