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

Viewed 1619

I've been having this particular problem with my nextjs eCommerce application for more than one week now and it just seems unsolvable for me and I must submit this project by this week as it is a project I am having from a web dev BootCamp that I am currently taking.

I have a shopping cart which has the information of all the products added to it and on my header, I have a shopping cart logo that should(required) display the amount of the items in my shopping cart on every page on the application. I could get the quantity to display but only when I am viewing the shopping cart, Once I click away from the shopping cart page then the quantity will be undefined. After doing a rigorous search on the internet I learned about the lifting state up in react and I have tried everything I can to pass this information through from their common parents which in my case is the _app.js and the same problem still persist. I read about the react context but the BootCamp is not allowing us to do this with the react context which has left me completely stuck on this. Please I am reaching out to anybody that can help me out, I don't know if I completely got everything wrong or if I just don't understand this react props concept or the particular thing that I am doing wrong in this regard.

I am sharing a screenshot here with the cart and my file structure. The code is already too much to copy and paste here so I am sharing the complete application link on code sandbox and anybody can take a look at the complete code there. I will appreciate any help I can get, please. Thanks in advance.

Here is the Codesandbox demo

how the quantity should be displayed across pages My file structure

please if there is any more information that I would need to provide, please just ask. Thanks a lot for any help.

Here is the code from my cart component where I am supposed to be updating the cartQuantity state variable.


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
    },
  };
}

I had the cartQuantity state variable and its setter inside the cart component but after reading about lifting state up I moved them to the _app.js component where I then passed them through props back to the cart component but it only shows the values in the cart when I am viewing the cart component.. everything I try doesn't change anything.. once I leave the cart component then the value becomes undefined.

below is the code from my _app.js where I passed the state variable props from:

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;


1 Answers

All eCommerce websites stores cart items into local storage, that's why every time you visit their website, you can see what has been already in the cart. The best way is to store cart items in local storage. since the cart icon will be inside the header and the header will be displayed on each page, inside useEffect read the local storage data.

First you need to write the items on localstorage. In your addToCart function (what ever you named it), when you add the items in your cart, write them to localstorage. (if you were using redux, best place would be inside addToCart action)

Since you are using next.js

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

I assume you are not using redux,so best way inside Header component in useEffect:

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") )
    : [];
}

      })
Related