So I'm trying to build a logic which will enable user to see more details about the specific product that was clicked on, but I only managed to make it appear in the URL for now. How can I achieve this?
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App.jsx';
import VinylClocks from './Components/VinylClocks.jsx';
import CustomClocks from './Components/CustomClocks.jsx';
import About from './Components/About.jsx';
import Contact from './Components/Contact.jsx';
import ProductPage from './Components/ProductPage.jsx';
import './index.css';
import {
createBrowserRouter,
RouterProvider,
Route,
} from "react-router-dom";
const router = createBrowserRouter([
{
path: "/",
element: <App/>,
},
{
path: "vinylclocks",
element: <VinylClocks/>,
},
{
path: "customclocks",
element: <CustomClocks/>,
},
{
path: "about",
element: <About/>,
},
{
path: "contact",
element: <Contact/>,
},
{
path: "cart",
element: <VinylClocks/>,
},
{
path: "productpage/:name",
element: <ProductPage/>,
},
]);
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<RouterProvider router={router} />
</React.StrictMode>,
)
import React, { useEffect, useMemo, useState } from "react";
import "./VinylClocks.css";
import Navbar from "./Navbar.jsx";
import Footer from "./Footer.jsx";
import SortButton from "./SortButton.jsx";
import { data } from "./data.js";
import Product from "./Product.jsx";
import FilterButton from "./FilterButton.jsx";
export default function VinylClocks() {
const [dataList, setDataList] = useState([]);
const [selectedCategory, setSelectedCategory] = useState("");
const [sortOption, setSortOption] = useState("latest");
// Add default value on page load
useEffect(() => {
setDataList(data);
}, []);
// Function to get filtered and sorted list
function getFilteredAndSortedList() {
let filteredList = dataList;
// Filter by category
if (selectedCategory) {
filteredList = dataList.filter((item) => item.cat === selectedCategory);
}
// Sort by
switch (sortOption) {
case "highToLow":
filteredList.sort((a, b) => b.price - a.price);
break;
case "lowToHigh":
filteredList.sort((a, b) => a.price - b.price);
break;
case "oldest":
filteredList.sort((a, b) => a.id - b.id);
break;
default:
// Latest or default sorting
case "latest":
filteredList.sort((a, b) => b.id - a.id);
break;
}
return filteredList;
}
// Avoid duplicate function calls with useMemo
var filteredAndSortedList = useMemo(getFilteredAndSortedList, [
selectedCategory,
sortOption,
dataList,
]);
function handleCategoryChange(option) {
setSelectedCategory(option);
}
function handleSortChange(option) {
setSortOption(option);
}
return (
<>
<header className="header">
<Navbar />
</header>
<div className="vc-container">
<div className="filter-sort">
<FilterButton onFilterChange={handleCategoryChange}/>
<SortButton onSortChange={handleSortChange} />
</div>
<div className="products-container">
<div className="catSort-container">
<form onFilterChange={handleCategoryChange} className="cat-form" action="#">
<h5>Categories:</h5>
<input type="checkbox" checked={selectedCategory === ""} onChange={() => setSelectedCategory("")} id="all" className="radio-input" name="" value=""></input>
<label htmlFor="all">All</label><br/>
<input type="checkbox" checked={selectedCategory === "movies"} onChange={() => setSelectedCategory( "movies")} id="movies" className="radio-input" name="" value="movies"></input>
<label htmlFor="movies">Movies</label><br/>
<input type="checkbox" checked={selectedCategory === "music"} onChange={() => setSelectedCategory("music")} id="music" className="radio-input" name="" value="music"></input>
<label htmlFor="music">Music</label><br/>
<input type="checkbox" checked={selectedCategory === "sport"} onChange={() => setSelectedCategory("sport")} id="sport" className="radio-input" name="" value="sport"></input>
<label htmlFor="sport">Sport</label><br/>
<input type="checkbox" checked={selectedCategory === "other"} onChange={() => setSelectedCategory("other")} id="other" className="radio-input" name="" value="other"></input>
<label htmlFor="other">Other</label>
</form>
<form onSortChange={handleSortChange} className="sort-form" action="#">
<h5>Sort by:</h5>
<h3>Date:</h3>
<input type="checkbox" checked={sortOption === "latest"} onChange={() => setSortOption("latest")}
id="latest" className="radio-input" name="" value="latest"></input>
<label>Latest</label><br/>
<input type="checkbox" checked={sortOption === "oldest"} onChange={() => setSortOption( "oldest")}
id="oldest" className="radio-input" name="" value="oldest"></input>
<label>Oldest</label><br/>
<h3>Price:</h3>
<input type="checkbox" checked={sortOption === "highToLow"} onChange={() => setSortOption("highToLow")}
id="lowest" className="radio-input" name="" value="highToLow"></input>
<label>Highest</label><br/>
<input type="checkbox" checked={sortOption === "lowToHigh"} onChange={() => setSortOption( "lowToHigh")}
id="highest" className="radio-input" name="" value="lowToHigh"></input>
<label>Lowest</label>
</form>
</div>
<div className="products">
{filteredAndSortedList.map((item, id) => (
<Product {...item} key={id} />
))}
</div>
</div>
</div>
<Footer />
</>
);
}
import React from "react";
import "./VinylClocks.css";
import { Link } from "react-router-dom";
export default function Product({name, price, img}) {
return (
<div className="products-card">
<div className="name-price-tag">
<Link to={`/productpage/${name}`}><img src={img}/></Link>
<div className="price-container">
<p className="product-name">{name}</p>
<span className="price">${price}</span>
</div>
</div>
</div>
)
}
import React from "react";
import "./ProductPage.css";
import { data } from "./data.js";
export default function ProductPage() {
return (
<>
{data.map((element, id) => (
<div className="productPage-container" key={id}>
<div className="image-container">
<img src={element.img} {...element}/>
</div>
<div className="about-product">
<span className="product-type">{element.type}</span>
<h2 className="product-name">{element.name}</h2>
<span className="product-price">{element.price}</span>
<p className="product-desc">{element.desc}</p>
</div>
</div>
))}
</>
)
}
p.s. i'm aware that ProductPage is just mapping the array all over again, that 's what i'm trying to figure out.
Use the useParams
hook to access the product's name
value from the route parameters.
{
path: "productpage/:name",
element: <ProductPage/>,
},
Use the current name
parameter to search the data
array for a matching element by name
property. Don't forget to account for missing data.
import React from "react";
import "./ProductPage.css";
import { useParams } from 'react-router-dom';
import { data } from "./data.js";
export default function ProductPage() {
const { name } = useParams();
const item = data.find(item => item.name === name);
if (!item) {
return <div>No item</div>;
}
return (
<div className="productPage-container" key={id}>
<div className="image-container">
<img src={item.img} {...element}/>
</div>
<div className="about-product">
<span className="product-type">{item.type}</span>
<h2 className="product-name">{item.name}</h2>
<span className="product-price">{item.price}</span>
<p className="product-desc">{item.desc}</p>
</div>
);
}
For details see: