Search code examples
react-hooksfetch-apiuse-effectuse-statereact-state-management

I want to Fetch products and send them on click to detail page with same data in Reactjs


I have fetched an fake api using fetch on ProductList.js file and when I click on any of the white hovered blocks I want to send the same data to ProductDetail.js. So for this I am checking the pathname and again fetching data on ProductDetail.js and matching the title using find method. So in the console I get the matched object but suddenly after that I get undefined in console. Check console. I have attached all the files for your reference and I am using [email protected] and json-server for fake api.

this is the index.js file and I have imported BrowserRouter here.

################# Index.js #################

import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
import "bootstrap/dist/css/bootstrap.min.css";
import "./index.css";
import App from "./App";

ReactDOM.render(
  <React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>,
  document.getElementById("root")
);

this is App.js file and from here I am sending the /:title

################# App.js #################

import React from "react";
import { Switch, Route } from "react-router";
import ProductList from "./components/ProductList";
import ProductDetail from "./components/ProductDetail";
import PageNotFound from "./components/PageNotFound";

const App = () => {
  return (
    <>
      <Switch>
        <Route exact={true} path="/" component={ProductList} />
        <Route path="/:title" component={ProductDetail} />
        <Route path="*" component={PageNotFound} />
      </Switch>
    </>
  );
};

export default App;

here i am fetching a fake api using json-server and displaying the list and sending the title onclick on the white hovered block. If you add css then you will come to know.

################# ProductList.js #################

import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
const API = "http://localhost:8000/data";

const ProductList = () => {
  const [products, setProducts] = useState();

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

  const getData = () => {
    fetch(API)
      .then((res) => res.json())
      .then((data) => setProducts(data))
      .catch((err) => console.log(err));
  };

  return (
    <>
      <div className="container">
        <div className="row">
          {products &&
            products.map((item) => {
              const { id, imageUrl } = item;
              return (
                <div key={id} className="col-sm-6">
                  <img className="img-fluid" src={imageUrl} alt="" />
                  <div className="box">
                    {item.details.map((item) => {
                      const classes = [
                        "",
                        "block1",
                        "block2",
                        "block3",
                        "block4",
                        "block5",
                      ];
                      const { id, title, subTitle, price } = item;
                      return (
                        <div key={id} className={`white-block ${classes[id]}`}>
                          <i className="fa fa-circle"></i>
                          <Link to={`/${title}`}>
                            <div className="block">
                              <h5>{title}</h5>
                              <h6>{subTitle}</h6>
                              <p>Rs.{price}</p>
                            </div>
                          </Link>
                        </div>
                      );
                    })}
                  </div>
                </div>
              );
            })}
        </div>
      </div>
    </>
  );
};

export default ProductList;

here i am receiving the title through useLocation pathname and I am matching the title in getProduct function. I receive the object but then suddenly I get undefined in console, don't know why and this is the issue I am facing.

################# ProductDetail.js #################

import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import { useLocation } from "react-router";

const ProductDetail = () => {
  const [product, setProduct] = useState([]);
  const { pathname } = useLocation();

  useEffect(() => {
    fetch("http://localhost:8000/data")
      .then((res) => res.json())
      .then((data) => {
        setProduct((prev) => {
          return { ...prev, mydata: data };
        });
      })
      .catch((err) => console.log(err));

    getProduct();
  }, []);

  console.log(product.mydata);
  console.log(pathname);

  const getProduct = () => {
    if (product.mydata) {
      product.mydata.map((item) => {
        const x = item.details.find(
          (items) => items.title === `${pathname.replace(/\\|\//g, "")}`
        );
        console.log(x);
        setProduct(x);
      });
    }
  };

  return (
    <>
      {product && (
        <div className="container">
          <div className="row">
            <div className="col-lg-6">
              <img
                className="img-fluid"
                src="https://www.ikea.com/ext/ingkadam/m/6f1cba42d7e45883/original/PH162921-crop001.jpg?f=s"
                alt=""
              />
            </div>
            <div className="col-lg-6">
              <h1>{product.title}</h1>
              <h2>{product.subTitle}</h2>
              <p>{product.price}</p>
              <Link to="/">BACK</Link>
            </div>
          </div>
        </div>
      )}
    </>
  );
};

export default ProductDetail;

here is my db.json file. I am using json-server

################# db.json #################

{
  "data": [
    {
      "id": 1,
      "imageUrl": "https://www.ikea.com/ext/ingkadam/m/6f1cba42d7e45883/original/PH162921-crop001.jpg?f=s",
      "details": [
        {
          "id": 1,
          "title": "Raw Pressery",
          "subTitle": "Raw Sub Title",
          "price": 111
        },
        {
          "id": 2,
          "title": "Wingreens",
          "subTitle": "Wingreens Sub Title",
          "price": 222
        },
        {
          "id": 3,
          "title": "Spice",
          "subTitle": "Spice Sub Title",
          "price": 333
        },
        {
          "id": 4,
          "title": "Impatient",
          "subTitle": "Impatient Sub Title",
          "price": 444
        },
        { "id": 5, "title": "Boat", "subTitle": "Boat Sub Title", "price": 555 }
      ]
    },
    {
      "id": 2,
      "imageUrl": "https://www.ikea.com/ext/ingkadam/m/16daa9f360ad6234/original/PH177140.jpg?f=l",
      "details": [
        {
          "id": 1,
          "title": "Table",
          "subTitle": "Table Sub Title",
          "price": 1111
        },
        {
          "id": 2,
          "title": "Chair",
          "subTitle": "Chair Sub Title",
          "price": 2222
        },
        {
          "id": 3,
          "title": "Fridge",
          "subTitle": "Fridge Sub Title",
          "price": 3333
        }
      ]
    }
  ]
}

this is my index.css. If you add this css you will get to see the white hovered blocks.

################# index.css #################

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
    "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
    sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

code {
  font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
    monospace;
}

.white-block {
  position: absolute;
  z-index: 9;
}
.block1 {
  top: 2%;
  left: 5%;
}
.block2 {
  top: 15%;
  left: 50%;
}
.block3 {
  top: 40%;
  left: 10%;
}
.block4 {
  top: 50%;
  right: 15%;
}
.block5 {
  top: 70%;
  left: 35%;
}
.fa-circle {
  color: red;
}
.block {
  background-color: white;
  padding: 1rem;
  opacity: 0;
}
.white-block:hover .block {
  opacity: 1;
}
.showAll {
  position: absolute;
  bottom: 5%;
  left: 10%;
  z-index: 10;
}

Solution

  • const getProduct = () => {
    fetch("http://localhost:8000/data")
      .then((res) => res.json())
      .then((data) => {
        if (data && data.length > 0) {
          data.map((items) => {
            const x = items.details.find((item) => {
              return item.title === `${pathname.replace(/\\|\//g, "")}`;
            });
    //Here I added a check if x has the value then only you set the product.
            if (x) {
              setProduct(x);
            }
          });
        } else {
          setProduct({});
        }
      })
      .catch((err) => console.log(err));
    

    };