I'm trying to build an ecommerce site product details page in React and React-Router-DOM v6. I have an API where I have some fields like name
, title
, id
, and others.
I already created a product page which display cards, now I want to build the product description page where useParams
is given to id
, but when I implement the code it shows undefined
.
If I put the id
value manually in URL in the browser I got the results in console.
Code and screen shots attached.
The first time when URL load on product page, console is also given
The second image when I clicked on card
The third image when I entered the manual value in URL, like "product/17"
Products
import { Col, Container, Row } from "react-bootstrap";
import Navbars from "../Components/Navbars";
import { useState, useEffect } from "react";
import Cookies from "js-cookie";
import "./Products.css";
import { ShoppingCart } from "lucide-react";
import { Link } from "react-router-dom";
function Products() {
const [products, setProducts] = useState([]);
const getProduct = async () => {
try {
const jwtToken = Cookies.get("jwt_token");
const apiUrl = "https://apis.ccbp.in/products";
const options = {
headers: {
Authorization: `Bearer ${jwtToken}`,
},
method: "GET",
};
const response = await fetch(apiUrl, options);
const data = await response.json();
setProducts(data.products);
console.log(data);
// console.log(data.products[16].id);
} catch (error) {
console.error("Error fetching data:", error);
}
};
useEffect(() => {
getProduct();
}, []);
return (
<>
<Navbars />
<Container fluid>
<Row>
<Col>
<Link to={`/products/${products.id}`}>
<div>
<h2 className="mains-heading"> All Products</h2>
</div>
<div className="p3-5 prods">
{products.map((item, index) => {
const { brand, image_url, price, rating, title } = item;
return (
<div key={index} className="product-container">
<img src={image_url} alt="" className="map-image" />
<div className="card-body">
<h5 className="card-title text-center pt-2">
{title.substr(0, 30)}
</h5>
<p className="card-text text-center">{`by ${brand}`}</p>
<p className="text-center price">{`₹ ${price}`}</p>
<p className="ratings text-center">{`⭐ ${rating}`}</p>
<div className="btns text-center">
<button className="btns">
Add to Cart{<ShoppingCart />}{" "}
</button>
</div>
</div>
</div>
);
})}
</div>
</Link>
</Col>
</Row>
</Container>
</>
);
}
export default Products;
ProductsDetailedView
import Products from "./Products";
import { Link, useParams } from "react-router-dom";
import { useState, useEffect } from "react";
import Cookies from "js-cookie";
function ProductsDetailPage() {
const [products, setProducts] = useState([]);
const params = useParams();
const { id } = params;
console.log(id);
// console.log(params);
const getProductDetails = async () => {
try {
const jwtToken = Cookies.get("jwt_token");
const apiUrl = "https://apis.ccbp.in/products/" + `${id}`;
const options = {
headers: {
Authorization: `Bearer ${jwtToken}`,
},
method: "GET",
};
const response = await fetch(apiUrl, options);
const data = await response.json();
setProducts(data.products);
// setProducts(data.products);
console.log(data);
// console.log(data.products[16].id);
} catch (error) {
console.error("Error fetching data:", error);
}
};
useEffect(() => {
getProductDetails();
}, [id]);
return (
<>
this is dynamic route
<h1> {id}</h1>
</>
// <div>
// <Link to= "/products ${id}"> </Link>
// </div>
);
}
export default ProductsDetailPage;
App.js
import "./App.css";
import "bootstrap/dist/css/bootstrap.min.css";
import Login from "./Components/Login";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import Home from "./Pages/Home";
import Page404 from "./Components/page404";
import Carts from "./Pages/Carts";
import Products from "./Pages/Products";
import Protected from "./Components/Protected";
import ProductsDetailPage from "./Pages/ProductsDetailPage";
function App() {
return (
<>
<BrowserRouter>
<Routes>
<Route path="/" element={<Protected Comp={Home} />} />
<Route path="/login" element={<Login />} />
<Route path="/carts" element={<Protected Comp={Carts} />} />
<Route path="/products" element={<Protected Comp={Products} />} />
<Route path="/*" element={<Page404 />} />
<Route path="/products/:id" element={<ProductsDetailPage />} />
</Routes>
</BrowserRouter>
</>
);
}
export default App;
products
is the array of product data, so products.id
is undefined.
Move the Link
into the mapped callback so you can correctly link from each specific product by product.id
Example:
function Products() {
const [products, setProducts] = useState([]);
...
return (
<>
<Navbars />
<Container fluid>
<Row>
<Col>
<div>
<h2 className="mains-heading"> All Products</h2>
</div>
<div className="p3-5 prods">
{products.map((product) => {
const { brand, id, image_url, price, rating, title } = product;
return (
{/* link to specific product id */}
<Link key={id} to={`/products/${id}`}>
<div className="product-container">
<img src={image_url} alt="" className="map-image" />
<div className="card-body">
<h5 className="card-title text-center pt-2">
{title.substr(0, 30)}
</h5>
<p className="card-text text-center">
by {brand}
</p>
<p className="text-center price">
₹ {price}
</p>
<p className="ratings text-center">
⭐ {rating}
</p>
<div className="btns text-center">
<button className="btns">
Add to Cart{<ShoppingCart />}
</button>
</div>
</div>
</div>
</Link>
);
})}
</div>
</Col>
</Row>
</Container>
</>
);
}