Reputation: 300
I'm trying to create path for component like this "/products?id=uniqueId" for my React Js Project. Unique id is taken from database and depends on which product was chosen. My code is
App.js
import "./styles.css";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import Products from "./Products"
import Home from "./Home"
export default function App() {
return (
<div className="App">
<Router>
<Switch>
<Route exact path="/">
{" "}
<Home/>
</Route>
<Route exact path="/products">
{" "}
<Products />
</Route>
</Switch>
</Router>
</div>
);
}
Home.js
import "./styles.css";
import React, { useState } from "react";
import { useHistory } from "react-router-dom";
const products = [
{
id: "1",
name: "Product 1"
},
{
id: "2",
name: "Product 2"
},
{
id: "3",
name: "Product 3"
}
];
export default function Home() {
const [id, setId] = useState();
const history = useHistory();
const handleProceed = (e) => {
// console.log(id, "home");
history.push("/products", { id });
};
return (
<div
style={{ display: "flex", flexDirection: "column", alignItems: "center" }}
>
<div>
{products.map((product) => {
return (
<button
onClick={(e) => {
setId(product.id);
}}
>
{product.name}{" "}
</button>
);
})}
</div>
<button onClick={handleProceed} style={{ width: "250px" }}>
{" "}
Proceed
</button>
</div>
);
}
Product.js
import "./styles.css";
import { useLocation } from "react-router-dom";
export default function Home() {
const location = useLocation();
const { id } = location.state || { id: "none" };
console.log(id);
return (
<div>
<p> Lorem Ipsum</p>
</div>
);
}
Idea is when user presses any button with product name in < Home /> component, page should be redirected to < Product /> component and path should be "/products?id=uniqueId" where uniqueId = to product.id. I was able to pass product.id from < Home /> to < Product /> using useHistory() and useLocation(), but I don't know how to make path display selected id. For example if user clicks Product 1 button, path to < Product /> should be "/products?id=1", if Product 2 was chosen path should reflect that as "/products?id=2" and so on.
Any help and suggestions are greatly appreciated.
Upvotes: 3
Views: 39414
Reputation: 202846
I suggest making the product id
part of the path versus placing it in the query string. As part of the path it requires almost nothing from your code to access whereas if you used the query string you would then need to parse the entire query string into a Map of key-value pairs.
Define your path to include an id
<Route path="/products/:id">
<Products />
</Route>
On the products page use useParams
react hook to access the id
Route match param.
const { id } = useParams();
Generate the path with id
parameter
const handleProceed = (e) => {
history.push(generatePath("/products/:id", { id }));
};
or if you prefer it raw
const handleProceed = (e) => {
history.push(`/products/${id}`));
};
I further suggest optimizing your routing by reordering your Route
components to specify the more specific paths first. This allows you to avoid passing the exact
prop to every route.
<Router>
<Switch>
<Route path="/products/:id">
<Products />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
</Router>
full demo code:
import { useState } from "react";
import {
BrowserRouter as Router,
generatePath,
Switch,
Route,
useHistory,
useParams
} from "react-router-dom";
const products = [
{
id: "1",
name: "Product 1"
},
{
id: "2",
name: "Product 2"
},
{
id: "3",
name: "Product 3"
}
];
const Products = () => {
const { id } = useParams();
console.log(id);
return (
<div>
<p>Lorem Ipsum</p>
<p>Id: {id}</p>
</div>
);
};
const Home = () => {
const [id, setId] = useState();
const history = useHistory();
const handleProceed = (e) => {
id && history.push(generatePath("/products/:id", { id }));
};
return (
<div
style={{ display: "flex", flexDirection: "column", alignItems: "center" }}
>
<div>
{products.map((product, i) => (
<button
key={i}
onClick={(e) => {
setId(product.id);
}}
>
{product.name}
</button>
))}
</div>
<button onClick={handleProceed} style={{ width: "250px" }}>
Proceed
</button>
</div>
);
};
export default function App() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<Router>
<Switch>
<Route path="/products/:id">
<Products />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
</Router>
</div>
);
}
react-router-dom@6
Compliant Version:import { useState } from "react";
import {
BrowserRouter as Router,
generatePath,
Routes,
Route,
useNavigate,
useParams,
} from "react-router-dom";
const products = [
{
id: "1",
name: "Product 1"
},
{
id: "2",
name: "Product 2"
},
{
id: "3",
name: "Product 3"
}
];
const Products = () => {
const { id } = useParams();
console.log(id);
return (
<div>
<p>Lorem Ipsum</p>
<p>Id: {id}</p>
</div>
);
};
const Home = () => {
const [id, setId] = useState();
const navigate = useNavigate();
const handleProceed = (e) => {
id && navigate(generatePath("/products/:id", { id }));
};
return (
<div
style={{ display: "flex", flexDirection: "column", alignItems: "center" }}
>
<div>
{products.map((product, i) => (
<button
key={i}
onClick={(e) => {
setId(product.id);
}}
>
{product.name}
</button>
))}
</div>
<button onClick={handleProceed} style={{ width: "250px" }}>
Proceed
</button>
</div>
);
};
export default function App() {
return (
<div className="App">
<Router>
<Routes>
<Route path="/products/:id" element={<Products />} />
<Route path="/" element={<Home />} />
</Switch>
</Router>
</div>
);
}
Upvotes: 14