Hello, I’m in the process of implementing React Router in my simple e-commerce application. While I’m learning, I’ve encountered a problem that I’d appreciate some help with.
I successfully fetched data from db.json using the following code:
useEffect(() => {
const fetchData = async () => {
const responseObject = await getCategories();
setCategories(responseObject);
}
fetchData();
}, []);
The ‘getCategories’ function retrieves data from the database and sets it in the state, and this part works perfectly.
Next, I have implemented the fetching of products, which also works well:
export const getProducts = (id) => {
return fetcher(`/products?catId=${id}`)
}
Now, the problem arises with routing. Here is my implementation of React Router:
<React.StrictMode>
<BrowserRouter>
<Routes>
<Route path="/" element={<App />} />
<Route path='basket' element={<Basket />} />
<Route path='checkout' element={<Checkout />} />
<Route path="products/:productId" element={<ProductDetail />} />
</Routes>
</BrowserRouter>
</React.StrictMode>
The routing itself works perfectly to and from each component. However, the main issue lies in the Link To hook that I’m using to link various components in my app, especially since I’m fetching products from the JSON using their specific ID:
<div className='category-products-title'>
<Link to={`/products/${id}`}>{title}</Link>
</div>
The Link to route points to http://localhost:3000/products/undefined instead of rendering the actual product ID. I’ve attempted querying the database, and the fetch works as expected. I anticipated that upon clicking the link, the page would route to the actual product based on the ID.
Here is the code for all commponents.
1.App.js
import React, { useState, useEffect } from 'react';
import './App.css';
import Category from './components/category';
import { getCategories, getProducts } from './fetcher';
import CategoryProduct from './components/categoryProduct';
function App() {
const [categories, setCategories] = useState({ errorMessage: '', data: [] });
const [products, setProducts] = useState({ errorMessage: '', data: [] });
useEffect(() => {
const fetchData = async () => {
const responseObject = await getCategories();
setCategories(responseObject);
}
fetchData();
}, []);
const handleCategoryClick = async (id) => {
const responseObject = await getProducts(id);
setProducts(responseObject);
}
const renderCategories = () => {
return categories.data.map(c =>
<Category
key={c.id}
id={c.id}
title={c.title}
onCategoryClick={() => handleCategoryClick(c.id)}
/>
)
}
const renderProducts = () => {
return products.data.map(p =>
<CategoryProduct
key={p.id}
title={p.title}
image={p.image}
specs={p.specs}
features={p.features}
price={p.price}
stock={p.stock}
/>
);
}
return (
<>
<header>My Simple Web App</header>
<section>
<nav>
{categories.errorMessage && <div>Error: {categories.errorMessage}</div> }
{categories && renderCategories()}
</nav>
<article>
<h1>Products</h1>
{products.errorMessage && <div>Error: {products.errorMessage}</div>}
{products && renderProducts()}
</article>
</section>
<footer>
footer
</footer>
</>
);
}
export default App;
- fetcher.js
const BASE_URL = "http://localhost:3001"
const fetcher = async (url, id = null) => {
let responseObject = { errorMessage: '', data: [] }
try {
const response = await fetch(BASE_URL + url)
if (!response.ok) {
throw new Error(`HTTP Error ${response.status}`);
}
const responseData = await response.json()
responseObject.errorMessage = ''
responseObject.data = responseData
}
catch (err) {
responseObject.errorMessage = err.message
}
return responseObject
}
export const getCategories = () => {
return fetcher("/categories")
}
export const getProducts = (id) => {
return fetcher(`/products?catId=${id}`)
}
- index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import ProductDetail from './components/productDetail'
import Checkout from './components/checkout'
import Basket from './components/basket'
import { BrowserRouter, Routes, Route } from 'react-router-dom';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<BrowserRouter>
<Routes>
<Route path="/" element={<App />} />
<Route path='basket' element={<Basket />} />
<Route path='checkout' element={<Checkout />}/>
<Route path="products/:productId" element={<ProductDetail />} />
</Routes>
</BrowserRouter>
</React.StrictMode>
);
reportWebVitals();
- categoryProduct.js
import React from 'react'
import { Link } from 'react-router-dom'
const CategoryProduct = ({id, title, image, specs, features, price, stock, dimensions}) => {
return (
<main>
<div className='category-products-title'>
<Link to={`/products/${id}`}>{title}</Link>
</div>
<figure>
<div className='category-product-image-container'>
<img src={`/category-images/${title}/${image}`} alt='product_image' />
</div>
</figure>
<aside className='category-product-details'>
<div className='category-product-info-dimensions'>
<h3>Dimensions</h3>
<label>{ specs.dimensions}</label>
</div>
{specs.capacity &&
<div className='category-product-info-capacity'>
<h3>Capacity</h3>
<label>{ specs.capacity}</label>
</div>
}
<div className='category-product-info-features'>
<h3>features</h3>
<ul>
{features?.map((f, i) => {
return <li key={`feature${i}`}>{f}</li>
})}
</ul>
</div>
</aside>
<aside className='category-product-finance'>
<div className='category-product-finance-price'>
$pound;(price)
</div>
<div className='category-product-info-stock'>
<label>Stock Level: {stock}</label>
<label>Free delivery</label>
</div>
<div className='category-product-action'>
<button>View Product</button>
<button>Add to basket</button>
</div>
</aside>
</main>
)
}
export default CategoryProduct
- This is my db.json
{
"categories": [
{ "id": 1, "title": "Fridges"},
{ "id": 2, "title": "Kettles"},
{ "id": 3, "title": "Televisions"},
{ "id": 4, "title": "Microwaves"},
{ "id": 5, "title": "Laptops"}
],
"products": [
{
"id": 1,
"catId": 1,
"title": "Beko White Fridge Freezer",
"price": 399.99,
"image": "Beko_white_fridge.jpg",
"specs": {
"dimensions": "180x70x60 cm",
"capacity": "300 liters"
},
"stock": 10
},
{
"id": 2,
"catId": 1,
"title": "Whirlpool White Fridge Freezer",
"price": 449.99,
"image": "whirlpool_fridge.jpg",
"specs": {
"dimensions": "175x75x65 cm",
"capacity": "350 liters"
},
"stock": 15
},
{
"id": 3,
"catId": 2,
"title": "Electric Kettle",
"price": 29.99,
"specs": {
"dimensions": "20x20x15 cm"
},
"stock": 20
},
{
"id": 4,
"catId": 2,
"title": "Stainless Steel Kettle",
"price": 39.99,
"specs": {
"dimensions": "25x25x20 cm"
},
"stock": 25
},
{
"id": 5,
"catId": 3,
"title": "Samsung 55 inch 4K TV",
"price": 799.99,
"specs": {
"dimensions": "125x80x10 cm"
},
"stock": 5
},
{
"id": 6,
"catId": 3,
"title": "Sony 65 inch OLED TV",
"price": 1499.99,
"specs": {
"dimensions": "140x85x5 cm"
},
"stock": 3
},
{
"id": 7,
"catId": 4,
"title": "Countertop Microwave",
"price": 99.99,
"specs": {
"dimensions": "50x40x30 cm"
},
"stock": 8
},
{
"id": 8,
"catId": 4,
"title": "Over-the-Range Microwave",
"price": 249.99,
"specs": {
"dimensions": "75x60x40 cm"
},
"stock": 12
},
{
"id": 9,
"catId": 5,
"title": "HP 15.6 inch Laptop",
"price": 899.99,
"specs": {
"dimensions": "38x25x2 cm"
},
"stock": 7
},
{
"id": 10,
"catId": 5,
"title": "Dell XPS 13 inch Laptop",
"price": 1299.99,
"specs": {
"dimensions": "30x20x1 cm"
},
"stock": 4
}
]
}
Any assistance would be greatly appreciated! Thank you!
3
Answers
Try changing id to productId
Please provide more context for your. Paste the content for the whole components. This might be a issue of scope whereby you’re passing id that is not defined within that component.
I have also seen that you have defined the url params as product id make sure you’re accessing it with the same name in the ProductDetail component.
It seems like you are using the id prop in the CategoryProduct component but are not passing it to that component in your App file.
Change
to