skip to Main Content

When i click on addproduct then it gives error of undefined.. here is my ProductDetails.js code
Why the product setting to localstorage is undefined

import axios from "axios";
import React, { useEffect, useState } from "react";
import { NavLink, useParams } from "react-router-dom";
import { useDispatch } from "react-redux";
import { addToCart } from "../Redux/action/action";
//import { useSelector } from "react-redux";

import { FaStar } from "react-icons/fa";
import Skeleton from "react-loading-skeleton";

const cartItemsFromStorage =
  JSON.parse(localStorage.getItem("CartItems")) || [];

const ProductDetails = () => {
  const { id } = useParams();
  //const cartItems = useSelector((state) => state.handleCart);
  const [isLoading, setIsLoading] = useState(false);
  const [product, setProduct] = useState(cartItemsFromStorage); 

  const dispatch = useDispatch();

  useEffect(() => {
    const fetchProduct = async () => {
      try {
        setIsLoading(true);
        const { data } = await axios.get(
          `https://fakestoreapi.com/products/${id}`
        );
        setProduct(data);
      } catch (error) {
        console.error("Error fetching product:", error);
      } finally {
        setIsLoading(false);
      }
    };

    fetchProduct();
  }, [id]);

  const addProduct = (product) => {
    if (product) {
      // Update the Redux store
      dispatch(addToCart(product));

      // Retrieve existing cart items from localStorage
      const existingCartItemsJSON = localStorage.getItem("CartItems");
      const existingCartItems = existingCartItemsJSON
        ? JSON.parse(existingCartItemsJSON)
        : [];

      // Ensure that existingCartItems is an array
      if (!Array.isArray(existingCartItems)) {
        console.error("Invalid existingCartItems:", existingCartItems);
        return;
      }

      // Add the new product to the existing cart items
      const updatedCartItems = [...existingCartItems, product];

      // Store the updated cart items back in localStorage
      localStorage.setItem("CartItems", JSON.stringify(updatedCartItems));
    } else {
      console.error("Invalid product:", product);
    }
  };

  const ShowProducts = () => (
    <div className="d-flex row" key={product.id}>
      <div className="col-md-6 col-sm-3 mt-5">
        <img
          src={product.image}
          alt={product.title}
          height="400px"
          width="400px"
        />
      </div>
      <div className="col-md-6 mt-5">
        <h4 className="text-uppercase text-black-50">{product.category}</h4>
        <h1 className="display-5">{product.title}</h1>
        <p className="lead fw-bolder">
          Rating {product.rating && product.rating.rate}
          <FaStar />
        </p>
        <h3 className="display-6 fw-bolder my-4">${product.price}</h3>
        <p className="lead">{product.description}</p>
        <button className="btn btn-primary" onClick={() => addProduct(product)}>
          Add to Cart
        </button>
        <NavLink to="/MyCart" className="btn btn-outline-dark ms-2">
          Go to Cart
        </NavLink>
      </div>
    </div>
  );

  return (
    <>
      <div className="container py-5">
        <div className="row">
          {isLoading ? (
            <>
              {" "}
              <div className="col-md-6">
                <Skeleton height={400} />
              </div>
              <div className="col-md-6">
                <Skeleton width={300} height={50} />
                <Skeleton height={75} />
                <Skeleton width={25} height={150} />
                <Skeleton height={50} />
                <Skeleton height={150} />
                <Skeleton height={50} width={100} />
                <Skeleton height={50} width={100} />
              </div>
            </>
          ) : (
            product && <ShowProducts />
          )}
        </div>
      </div>
    </>
  );
};

export default ProductDetails;

Here is my Reducer.js code Where i defined addToCart() function

import { ADD_TO_CART, REMOVE_FROM_CART } from "../action/action-type";

const cart = []

const handleCart = (state = cart, action) => {
  const product = action.payload;

  switch (action.type) {
    case ADD_TO_CART:
      const existingProduct = state.find((item) => item.id === product.id);

      if (existingProduct) {
        return state.map((item) =>
          item.id === product.id ? { ...item, qty: item.qty + 1 } : item
        );
      } else {
        const product = action.payload;
        return [
          ...state,

          {
            ...product,
            qty: 1,
          },
        ];
      }

    case REMOVE_FROM_CART:
      const existingProductToRemove = state.find(
        (item) => item.id === product.id
      );
      if (existingProductToRemove.qty === 1) {
        return state.filter((item) => item.id !== product.id);
      } else {
        return state.map(
          (item) =>
            item.id === product.id ? { ...item, qty: item.qty - 1 } : item,
          localStorage.setItem("CartItems", JSON.stringify(state.cart))
        );
      }

    default:
      return state;
  }
};

export default handleCart;

when i click on addProduct it is giving undefind error i think it is getting undefined items from local storage or the data sets as undefined..
Please can someone provide me solution stucked in it from 2 days.

2

Answers


  1. there are a few things to consider in your code.

    In your component ProductDetails.js, the initial state for product is being set to cartItemsFromStorage, which is defined outside the component. This could be causing the undefined error. It seems you intended to initialize product with an empty object {} instead. You should replace this line:

    const [product, setProduct] = useState(cartItemsFromStorage);
    

    with

    const [product, setProduct] = useState({});
    

    The addProduct function expects a parameter product, but it’s being called without any parameter. When you’re calling it in the JSX button’s onClick handler, make sure you’re passing the product object as an argument:

    <button className="btn btn-primary" onClick={() => addProduct(product)}>Add to Cart</button>
    

    In your Redux reducer (handleCart.js), you have the following line inside the REMOVE_FROM_CART case:

    localStorage.setItem("CartItems", JSON.stringify(state.cart));
    

    you’re trying to store the updated cart items into localStorage, but you’re using state.cart, which doesn’t exist. It should be:

    localStorage.setItem("CartItems", JSON.stringify(state));
    

    Hopefully this will fix your error. But Also make your API calling has some data.

    Login or Signup to reply.
  2. The problem is simply because cartItemsFromStorage return an array but you set it to product which is an object. So, the {product && ShowProduct()} always renders, then you try to access things like product.title it will show as undefined

    You just need to refactor a bit to make it works:

    const [product, setProduct] = useState();
    
    useEffect(() => {
      const fetchProduct = async () => {
        // check local storage cache
        // should check cache in useEffect on `id` change
        // otherwise it only gets the cache the first time the file is loaded
        const cartItemsFromStorage =
          JSON.parse(localStorage.getItem("CartItems")) || [];
        const product = cartItemsFromStorage.find((item) => item.id === id);
    
        // if product exists, get data from cache
        if (product) {
          setProduct(product);
          return;
        }
    
        // if product does not exist, fetch api
        try {
          setIsLoading(true);
          const { data } = await axios.get(
            `https://fakestoreapi.com/products/${id}`
          );
          setProduct(data);
        } catch (error) {
          console.error("Error fetching product:", error);
        } finally {
          setIsLoading(false);
        }
      };
    
      fetchProduct();
    }, [id]);
    

    Look at your code I can tell that you are learning React. I would like to suggest some good practices for caching. In professional works, caching should be manage by cache manager package like @tanstack/query or in your case you’re using redux, a similar option is redux-query (or @reduxjs/toolkit/query if you’re using @reduxjs/toolkit). But it is totally fine to try the manual methods like you did first to understand how React works with browser APIs.

    Have a nice day!

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