Home > front end >  React Router Dom Issue - localhost:3000/products to localhost:3000/products/2 is working fine but wh
React Router Dom Issue - localhost:3000/products to localhost:3000/products/2 is working fine but wh

Time:02-26

I have an Index.js where I am getting data via async function and sending data to ProductList Component and from ProductList Component I am sending an id onclick of product Image and then I get Product Detail Page a Single Product. I get Single Product with no issues but when I refresh then I get an error in console saying - Cannot read properties of undefined (reading 'filter') I have used REDUX.

INDEX.JS

import React, { useState, useEffect } from "react";
// import PageNotFound from "../other/page-not-found/PageNotFound";
import ProductList from "./product-list/ProductList";
import ProductFilter from "./product-filter/ProductFilter";
import ProductSearch from "./product-search/ProductSearch";
import Cart from "./cart/Cart";
// import MobileFilterModal from "./mobile-filter-modal/MobileFilterModal";
import { useDispatch, useSelector } from "react-redux";
import { setProductListData } from "../../redux/actions/product-list-actions/ProductListActions";
import Loading from "../other/loading/Loading";
import Skeleton from "../other/skeletons/Skeleton";
// import PageNotFound from "../other/page-not-found/PageNotFound";

const Index = () => {
  const [isLoading, setIsLoading] = useState(true);
  const dispatch = useDispatch();
  const filters = useSelector(
    (state) => state.productList.productListData.filters
  );
  const products = useSelector(
    (state) => state.productList.productListData.products
  );
  // console.log(products);

  useEffect(() => {
    getProductsData();
  }, []);

  const getProductsData = async () => {
    try {
      const response = await fetch("http://localhost:8000/productListing");
      const data = await response.json();
      if (data) {
        setIsLoading(false);
        dispatch(setProductListData(data));
      } else {
        setIsLoading(true);
      }
    } catch (error) {
      console.log(error);
    }
  };

  // if (isLoading) {
  //   return <Loading loadingProductList={"products"} />;
  // }

  return (
    <>
      {filters && products && (
        <main>
          <div className="dvMain">
            <div className="container-fluid">
              <div className="row">
                <div className="dvFilters col-lg-3 col-xl-2 d-none d-lg-block text-right">
                  <div className="sticky-top" style={{ top: "90px" }}>
                    {isLoading
                      ? filters.map((filter) => {
                          return (
                            <Skeleton key={filter.id} filterId={filter.id} />
                          );
                        })
                      : filters &&
                        filters.map((filter) => {
                          return <ProductFilter key={filter.id} {...filter} />;
                        })}
                  </div>
                </div>
                <div className="dvProducts col-lg-6 col-xl-8">
                  <div className="row">
                    <div className="dvSearch col-sm-9 col-md-10 col-lg-12 mb-3">
                      {isLoading ? (
                        <Skeleton productSearch={"productSearch"} />
                      ) : (
                        <ProductSearch />
                      )}
                    </div>
                    <div className="dvFilterMobile col-sm-3 col-md-2 d-lg-none text-center text-sm-right mb-3">
                      <a
                        href=""
                        className="btn bg-light w-100"
                        data-toggle="modal"
                        data-target="#mobileFiltersModal"
                      >
                        <i className="fa fa-filter"></i>Filter
                      </a>
                    </div>
                  </div>
                  <div className="row">
                    {isLoading
                      ? products.map((product) => {
                          return (
                            <Skeleton key={product.id} productId={product.id} />
                          );
                        })
                      : products &&
                        products.map((product) => {
                          return <ProductList key={product.id} {...product} />;
                        })}
                  </div>
                </div>
                {isLoading ? <Skeleton cart={"cart"} /> : <Cart />}
              </div>
            </div>
          </div>
        </main>
      )}
    </>
  );
};

export default Index;

PRODUCTLIST.JS

import React from "react";
import { Link } from "react-router-dom";
import "./productlist.css";

const ProductList = ({ id, img, pack, price, size, heading, description }) => {
  // console.log(id, img, pack, price, size, heading, description);
  // console.log(id, img, pack, price, size, heading, description);

  return (
    <>
      <div className="col-6 col-md-4 col-lg-6 col-xl-3 mb-4">
        <div className="border border-light shadow-sm p-1 h-100">
          <div className="image">
            <p className="packs">pack of 5</p>
            <div className="bg-light text-center pt-2 pb-2 mb-1">
              <Link className="d-inline-block" to={`/${id}`}>
                <img src={img} className="img-fluid" alt={heading} />
              </Link>
            </div>
            <h5 className="heading-5 text-center">{heading}</h5>
          </div>
          <div className="description d-flex justify-content-between mb-1">
            <div className="paragraph">
              <p>{size}</p>
            </div>
            <div className="paragraph mr-2">
              <span>
                <i className="fa fa-inr"></i>
                <span>{price}</span>
              </span>
            </div>
          </div>
          <div className="addBtn text-center">
            <button className="btn btn-white w-100" href="detail.html">
              Add to Bag
            </button>
          </div>
        </div>
      </div>
    </>
  );
};

export default ProductList;

PRODUCTDETAIL.JS

import React, { useState, useEffect } from "react";
import Cart from "../cart/Cart";
import "./productdetail.css";
import Loading from "../../other/loading/Loading";
import PageNotFound from "../../other/page-not-found/PageNotFound";
import { Link, useParams } from "react-router-dom";
import { useDispatch, useSelector } from "react-redux";
import { setSelectedProduct } from "../../../redux/actions/product-detail-actions/ProductDetailActions";

const ProductDetail = () => {
  const [isLoading, setIsLoading] = useState(true);
  const { id } = useParams();
  const dispatch = useDispatch();
  const products = useSelector(
    (state) => state.productList.productListData.products
  );
  const singleProduct = useSelector(
    (state) => state.singleProduct.singleProduct
  );

  // console.log(id);
  // console.log(products);
  // console.log(singleProduct);

  useEffect(() => {
    getProductId();
  }, []);

  const getProductId = () => {
    window.scrollTo(0, 0);
    // localStorage.setItem("setProducts", JSON.stringify(products));
    // let getProducts = JSON.parse(localStorage.getItem("setProducts"));
    products ? setIsLoading(false) : setIsLoading(true);
    let singleProduct = [];
    singleProduct = products.filter((product) => {
      return product.id === parseInt(id);
    });
    // console.log(singleProduct);
    dispatch(setSelectedProduct(singleProduct));
  };

  if (isLoading) {
    return <Loading loadingProductDetail={"product detail"} />;
  }

  return (
    <>
      {singleProduct && (
        <main>
          <div className="dvMain">
            <div className="container">
              <div className="row">
                {singleProduct ? (
                  singleProduct.map((product) => {
                    const {
                      id,
                      img,
                      heading,
                      description,
                      category,
                      price,
                      size,
                    } = product;
                    return (
                      <div key={id} className="col-lg-9">
                        <div className="row">
                          <div className="dvProducts col-12">
                            <div className="row">
                              <div className="dvBack col-12">
                                <Link
                                  to="/products"
                                  className="mb-1 d-inline-block"
                                >
                                  <i className="fa fa-angle-left"></i>
                                  <span> Back</span>
                                </Link>
                              </div>
                              <div className="col-12">
                                <div className="row">
                                  <div className="col-12 col-md-6 col-xl-4 mb-3">
                                    <div className="border border-light shadow-sm p-1 h-100 d-flex justify-content-center align-items-center">
                                      <div className="bg-light text-center h-100">
                                        <a className="d-inline-block h-100">
                                          <img
                                            src={img}
                                            className="img-fluid"
                                            alt={heading}
                                          />
                                        </a>
                                      </div>
                                    </div>
                                  </div>
                                  <div className="col-12 col-md-6 col-xl-8 d-flex mb-3 mb-xl-0">
                                    <div className="m-md-auto">
                                      <div>
                                        <h2 className="heading-2">{heading}</h2>
                                      </div>
                                      <div className="mb-2">
                                        <i className="fa fa-star text-warning d-inline-block"></i>
                                        <i className="fa fa-star text-warning d-inline-block"></i>
                                        <i className="fa fa-star text-warning d-inline-block"></i>
                                        <i className="fa fa-star-o text-warning d-inline-block"></i>
                                        <i className="fa fa-star-o text-warning d-inline-block"></i>
                                      </div>
                                      <div className="mb-3">
                                        <p className="paragraph">
                                          {description}
                                        </p>
                                      </div>
                                      <div className="d-flex mb-3">
                                        <div className="mr-2">
                                          <h5 className="heading-5 d-inline-block mb-1 mr-1">
                                            Size:
                                          </h5>
                                          <span className="paragraph d-inline-block">
                                            {size}
                                          </span>
                                        </div>
                                        <div className="mr-2 ml-2">
                                          <h5 className="heading-5 d-inline-block mb-1 mr-1">
                                            Category:
                                          </h5>
                                          <span className="paragraph d-inline-block">
                                            {category}
                                          </span>
                                        </div>
                                        <div className="ml-2">
                                          <h5 className="heading-5 d-inline-block mb-1 mr-1">
                                            Price:
                                          </h5>
                                          <span className="paragraph d-inline-block">
                                            <i className="fa fa-inr"></i>
                                            <span className="d-inline-block">
                                              {price}.00
                                            </span>
                                          </span>
                                        </div>
                                      </div>
                                      <div>
                                        <button
                                          className="btn btn-black"
                                          href="/-"
                                        >
                                          Add to Bag
                                        </button>
                                      </div>
                                    </div>
                                  </div>
                                </div>
                              </div>
                            </div>
                          </div>
                          <div className="dvTabs col-12 mt-4">
                            <div className="row">
                              <div className="col-12">
                                <div className="row">
                                  <div className="col-12">
                                    <ul
                                      className="nav nav-tabs"
                                      id="myTab"
                                      role="tablist"
                                    >
                                      <li className="nav-item">
                                        <a
                                          className="heading-5 nav-link active"
                                          id="description-tab"
                                          data-toggle="tab"
                                          href="#description"
                                          role="tab"
                                          aria-controls="description"
                                          aria-selected="true"
                                        >
                                          Description
                                        </a>
                                      </li>
                                    </ul>
                                  </div>
                                </div>
                                <div
                                  className="dvTabsContent tab-content row"
                                  id="myTabContent"
                                >
                                  <div
                                    className="tab-pane fade show active py-3 col-12"
                                    id="description"
                                    role="tabpanel"
                                    aria-labelledby="description-tab"
                                  >
                                    <div className="row">
                                      <div className="col-12">
                                        <div className="table-responsive">
                                          <table className="table table-striped table-bordered">
                                            <thead>
                                              <tr>
                                                <th
                                                  className="heading-5"
                                                  scope="col"
                                                >
                                                  Details
                                                </th>
                                                <th
                                                  className="heading-5"
                                                  scope="col"
                                                >
                                                  Weight
                                                </th>
                                                <th
                                                  className="heading-5"
                                                  scope="col"
                                                >
                                                  1Glass 250ml
                                                </th>
                                              </tr>
                                            </thead>
                                            <tbody>
                                              <tr>
                                                <td>Amount per serving</td>
                                                <td>250ml</td>
                                                <td></td>
                                              </tr>
                                            </tbody>
                                          </table>
                                        </div>
                                      </div>
                                    </div>
                                  </div>
                                </div>
                              </div>
                            </div>
                          </div>
                        </div>
                      </div>
                    );
                  })
                ) : (
                  <PageNotFound />
                )}
                <Cart id={id} />
              </div>
            </div>
          </div>
        </main>
      )}
    </>
  );
};

export default ProductDetail;

APP.JS

import React from "react";
import "./App.css";
import { BrowserRouter, Switch, Route } from "react-router-dom";
import Homepage from "./containers/home-page/index";
import Header from "./containers/header/Header";
import Footer from "./containers/footer/Footer";
// import ProductListing from "./containers/products/product-list/ProductList";
import ProductDetail from "./containers/products/product-detail/ProductDetail";
import PageNotFound from "./containers/other/page-not-found/PageNotFound";
import Account from "./containers/account/index";
import Checkout from "./containers/checkout/Index";
import LoginModal from "./containers/auth/modal/login-modal/LoginModal";
import SignupModal from "./containers/auth/modal/signup-modal/SignupModal";
import MobileCartModal from "./containers/products/mobile-cart-modal/MobileCartModal";
import Index from "./containers/products";

const App = () => {
  return (
    <>
      <BrowserRouter>
        <Header />
        <main>
          <Switch>
            <Route path="/" exact component={Homepage} />
            <Route path="/products" component={Index} />
            <Route path="/:id" children={<ProductDetail />} />
            <Route path="/checkout" component={Checkout} />
            <Route path="/account" component={Account} />
            <Route path="*" component={PageNotFound} />
          </Switch>
        </main>
        <Footer />
        <LoginModal />
        <SignupModal />
        <MobileCartModal />
      </BrowserRouter>
    </>
  );
};

export default App;

PRODUCTDETAILACTIONS.JS

export const setSelectedProduct = (product) => {
  return {
    type: "SET_SELECTED_PRODUCT",
    payload: product,
  };
};

PRODUCTDETAILREDUCER.JS

const initialState = {
  singleProduct: [],
};

export const productDetailReducer = (state = initialState, action) => {
  switch (action.type) {
    case "SET_SELECTED_PRODUCT":
      return {
        ...state,
        singleProduct: action.payload,
      };
    default:
      return state;
  }
};

REDUCER INDEX.JS

import { combineReducers } from "redux";
import { aboutUsReducer } from "./aboutus-reducers/AboutUsReducers";
import { footerReducer } from "./footer-reducers/FooterReducer";
import { headerReducer } from "./header-reducers/headerReducer";
import { mainSliderReducer } from "./homepage-reducers/mainSliderReducer";
import { youtubeReducer } from "./homepage-reducers/YoutubeReducer";
import { productDetailReducer } from "./product-detail/ProductDetailReducer";
import { productListReducer } from "./product-list-reducers/ProductListReducer";
import { productSliderData } from "./productslider-reducers/ProductSliderReducer";
import { teamReducer } from "./team-reducers/TeamReducers";

const rootReducer = combineReducers({
  header: headerReducer,
  mainSlider: mainSliderReducer,
  youtube: youtubeReducer,
  aboutUs: aboutUsReducer,
  productSlider: productSliderData,
  team: teamReducer,
  footer: footerReducer,
  productList: productListReducer,
  singleProduct: productDetailReducer,
});

export default rootReducer;

STORE.JS

import { createStore } from "redux";
import rootReducer from "./reducers/index";

const store = createStore(
  rootReducer,
  {},
  window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);

export default store;

PRODUCTLISTACTIONS.JS

export const setProductListData = (data) => {
  return {
    type: "SET_PRODUCT_LIST_DATA",
    payload: data,
  };
};

PRODUCTLISTREDUCER.JS

const initialState = {
  productListData: {},
};

export const productListReducer = (state = initialState, action) => {
  switch (action.type) {
    case "SET_PRODUCT_LIST_DATA":
      return {
        ...state,
        productListData: action.payload,
      };
    default:
      return state;
  }
};

DB.JSON I am fetching productListing from json file which look like this enter image description here

CodePudding user response:

From what I can see, the ProductDetails component is missing a dependency on the id route param to select the correct products to filter, and I am assuming that since the index file is fetching and populating the store each time it mounts that perhaps the initial products state is null, undefined, or some other non-array value that is missing the filter method.

Add the missing id route param to the useEffect hook's dependency array so products are fetched when the route changes.

const { id } = useParams();

...

useEffect(() => {
  getProductId(id);
}, [id]);

Since it appears that products is potentially undefined, i.e. falsey, you should account for then when attempting to filter the array. Provide a fallback empty array to filter from if products is falsey.

const getProductId = (id) => {
  window.scrollTo(0, 0);
  products ? setIsLoading(false) : setIsLoading(true);

  const singleProduct = (products || []).filter((product) => {
    return product.id === parseInt(id);
  });

  dispatch(setSelectedProduct(singleProduct));
};

You didn't include any of your Redux code (i.e. actions/reducers/store configuration) but a suggestion is to always provide a consistent state invariant. For example, always returning an array of products and using the array length and some "isLoading" state to determine when it's safe to filter the state. At a minimum, if it's guaranteed that products is always an array then all array methods will always work as expected, regardless of where the app is in its lifecycle and loading data/state.

CodePudding user response:

Hey I found this solution. I fetched products through async function instead of getting products back from useSelector. Please check ProductDetail Component below.

PRODUCTDETAIL.JS

import React, { useState, useEffect } from "react";
import Cart from "../cart/Cart";
import "./productdetail.css";
import Loading from "../../other/loading/Loading";
import PageNotFound from "../../other/page-not-found/PageNotFound";
import { Link, useParams } from "react-router-dom";
import { useDispatch, useSelector } from "react-redux";
import { setSelectedProduct } from "../../../redux/actions/product-detail-actions/ProductDetailActions";

const ProductDetail = () => {
  const [loading, setLoading] = useState(true);
  const { id } = useParams();
  const dispatch = useDispatch();
  const singleProduct = useSelector(
    (state) => state.singleProduct.singleProduct
  );

  useEffect(() => {
    getProductListingData();
  }, []);

  const getProductListingData = async () => {
    try {
      const response = await fetch("http://localhost:8000/productListing");
      const data = await response.json();
      if (data) {
        setLoading(false);
        getProductID(data.products);
      } else {
        // setProducts("PRODUCT LISTING DATA NOT FOUND");
        setLoading(true);
        console.log("PRODUCT LISTING DATA NOT FOUND");
      }
    } catch (error) {
      console.log(error);
    }
  };

  const getProductID = (products) => {
    window.scroll(0, 0);
    let singleProduct = [];
    singleProduct = products.filter((item) => {
      return item.id === parseInt(id);
    });
    dispatch(setSelectedProduct(singleProduct));
  };

  if (loading) {
    return <Loading loadingProductDetail="Loading Product Details" />;
  }

  console.log(id);
  console.log(singleProduct);

  return (
    <>
      <main>
        <div className="dvMain">
          <div className="container">
            <div className="row">
              {singleProduct.length > 0 &&
                singleProduct.map((product) => {
                  const {
                    id,
                    img,
                    heading,
                    description,
                    pack,
                    price,
                    size,
                    category,
                    detailsTable,
                  } = product;
                  return (
                    <div key={id} className="col-lg-9">
                      <div className="row">
                        <div key={id} className="dvProducts col-12">
                          <div className="row">
                            <div className="dvBack col-12">
                              <Link
                                to="/products"
                                className="mb-1 d-inline-block"
                              >
                                <i className="fa fa-angle-left"></i>
                                <span> Back</span>
                              </Link>
                            </div>
                            <div className="col-12">
                              <div className="row">
                                <div className="col-12 col-md-6 col-xl-4 mb-3">
                                  <div className="border border-light shadow-sm p-1 h-100 d-flex justify-content-center align-items-center">
                                    <div className="bg-light text-center h-100">
                                      <a className="d-inline-block h-100">
                                        <img
                                          src={img}
                                          className="img-fluid"
                                          alt={heading}
                                        />
                                      </a>
                                    </div>
                                  </div>
                                </div>
                                <div className="col-12 col-md-6 col-xl-8 d-flex mb-3 mb-xl-0">
                                  <div className="m-md-auto">
                                    <div>
                                      <h2 className="heading-2">{heading}</h2>
                                    </div>
                                    <div className="mb-2">
                                      <i className="fa fa-star text-warning d-inline-block"></i>
                                      <i className="fa fa-star text-warning d-inline-block"></i>
                                      <i className="fa fa-star text-warning d-inline-block"></i>
                                      <i className="fa fa-star-o text-warning d-inline-block"></i>
                                      <i className="fa fa-star-o text-warning d-inline-block"></i>
                                    </div>
                                    <div className="mb-3">
                                      <p className="paragraph">{description}</p>
                                    </div>
                                    <div className="d-flex mb-3">
                                      <div className="mr-2">
                                        <h5 className="heading-5 d-inline-block mb-1 mr-1">
                                          Pack:
                                        </h5>
                                        <span className="paragraph d-inline-block">
                                          {pack}
                                        </span>
                                      </div>
                                      <div className="mr-2">
                                        <h5 className="heading-5 d-inline-block mb-1 mr-1">
                                          Size:
                                        </h5>
                                        <span className="paragraph d-inline-block">
                                          {size}
                                        </span>
                                      </div>
                                      <div className="mr-2 ml-2">
                                        <h5 className="heading-5 d-inline-block mb-1 mr-1">
                                          Category:
                                        </h5>
                                        <span className="paragraph d-inline-block">
                                          {category}
                                        </span>
                                      </div>
                                      <div className="ml-2">
                                        <h5 className="heading-5 d-inline-block mb-1 mr-1">
                                          Price:
                                        </h5>
                                        <span className="paragraph d-inline-block">
                                          <i className="fa fa-inr"></i>
                                          <span className="d-inline-block">
                                            {price}.00
                                          </span>
                                        </span>
                                      </div>
                                    </div>
                                    <div>
                                      <button
                                        className="btn btn-black"
                                        href="/-"
                                      >
                                        Add to Bag
                                      </button>
                                    </div>
                                  </div>
                                </div>
                              </div>
                            </div>
                          </div>
                        </div>
                        <div className="dvTabs col-12 mt-4">
                          <div className="row">
                            <div className="col-12">
                              <div className="row">
                                <div className="col-12">
                                  <ul
                                    className="nav nav-tabs"
                                    id="myTab"
                                    role="tablist"
                                  >
                                    <li className="nav-item">
                                      <a
                                        className="heading-5 nav-link active"
                                        id="description-tab"
                                        data-toggle="tab"
                                        href="#description"
                                        role="tab"
                                        aria-controls="description"
                                        aria-selected="true"
                                      >
                                        Description
                                      </a>
                                    </li>
                                  </ul>
                                </div>
                              </div>
                              <div
                                className="dvTabsContent tab-content row"
                                id="myTabContent"
                              >
                                <div
                                  className="tab-pane fade show active py-3 col-12"
                                  id="description"
                                  role="tabpanel"
                                  aria-labelledby="description-tab"
                                >
                                  <div className="row">
                                    <div className="col-12">
                                      <div className="table-responsive">
                                        <table className="table table-striped table-bordered">
                                          <thead>
                                            <tr>
                                              <th
                                                className="heading-5"
                                                scope="col"
                                              >
                                                Details
                                              </th>
                                              <th
                                                className="heading-5"
                                                scope="col"
                                              >
                                                Weight
                                              </th>
                                              <th
                                                className="heading-5"
                                                scope="col"
                                              >
                                                1Glass 250ml
                                              </th>
                                            </tr>
                                          </thead>
                                          <tbody>
                                            <tr>
                                              <td>Amount per serving</td>
                                              <td>250ml</td>
                                              <td></td>
                                            </tr>
                                          </tbody>
                                        </table>
                                      </div>
                                    </div>
                                  </div>
                                </div>
                              </div>
                            </div>
                          </div>
                        </div>
                      </div>
                    </div>
                  );
                })}
              <Cart id={id} />
            </div>
          </div>
        </div>
      </main>
    </>
  );
};

export default ProductDetail;
  • Related