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
CodeSandbox
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.
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>
Demo
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>
);
}
Related
in react I want to run an application-related react-router; that is fine; the problem is that specific code, such as "react-router-dom": "6.8.1," does not run in the latest react version and throws an error.
"Element type is invalid: expected a string (for built-in components)
or a class/function (for composite components) but got: undefined. You
likely forgot to export your component from the file it's defined in,
or you might have mixed up default and named imports."
But if you change the version, it's working fine, like in "react-router-dom": "5.2.1", This code is working fine, ,so my question is in "react-router-dom": :6.8.1 How to successfully run that code?in my App.js in code structure which thing should I have to change that code will run perfectly in 6.8.1 too?
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();
console.log(history)
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" }}>
Click
</button>
</div>
);
};
export default function App() {
return (
<div className="App">
<header>Heading</header>
<Router>
<Switch>
<Route path="/products/:id">
<Products />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
</Router>
</div>
);
}
For React Router v6, useNavigate should be used instead of useHistory (which no longer exists).
const navigate = useNavigate();
// ...
id && navigate(generatePath("/products/:id", { id }));
react-router#6 brought with it a ton of breaking changes. You should review the Upgrading from v5 migration guide for the full details.
The Switch component was replaced by the Routes component which is required to directly wrap Routes you are rendering, and the useHistory hook was replaced by the useNavigate hook.
import { useState } from "react";
import {
BrowserRouter as Router,
generatePath,
Routes,
Route,
useNavigate,
useParams
} from "react-router-dom";
useNavigate returns a navigate function instead of a "history" object. Call navigate to issue imperative navigation actions.
const Home = () => {
const [id, setId] = useState();
const navigate = useNavigate();
const handleProceed = () => {
navigate(generatePath("/products/:id", { id }));
};
return (
<div
style={{ display: "flex", flexDirection: "column", alignItems: "center" }}
>
<div>
{products.map((product) => (
<button
key={product.id}
onClick={() => {
setId(product.id);
}}
>
{product.name}
</button>
))}
</div>
<button
type="button
disabled={!id}
onClick={handleProceed}
style={{ width: "250px" }}
>
Click
</button>
</div>
);
};
The Route component API/props changed, all the routed content is rendered on the Route component's element prop.
export default function App() {
return (
<div className="App">
<header>Heading</header>
<Router>
<Routes>
<Route path="/products/:id" element={<Products />} />
<Route path="/" element={<Home />} />
</Routes>
</Router>
</div>
);
}
I have created react app. I have directory components with folder pages where I have FindAllMetadata component in which I am making GET request and getting all metadata from API. I am passing loadedMetadata to AllMetadataTable as props. AllMetadataTable component is located in other folder called data. There I am displaying some information about fetched metadata items in table ( Creator, Time Created, Format ) and other fetched properties I am not displaying. In a table aside of the every fetched metadata I have + button which when clicked makes link ( route ) to the new page where I want display all information about fetched metadata, single clicked metadata item. Details component is located in folder pages. I want to display clicked Metadata from AllMetadataTable in Details page or in Metadata component.
Here is my App.js :
import React, { Suspense } from 'react';
import { Switch, Route, BrowserRouter } from 'react-router-dom';
import classes from './App.module.css'
import LoadingSpinner from './components/UI/LoadingSpinner';
import Layout from './components/layout/Layout';
import Footer from './components/layout/Footer';
import RequestMenu from './components/UI/RequestMenu';
// load components only when user gets to them
const Dashboard = React.lazy(() => import('./components/pages/Dashboard'));
const NewData = React.lazy(() => import('./components/pages/NewData'));
const NotFound = React.lazy(() => import('./components/pages/NotFound'));
const FindAllReceivedRequests = React.lazy(() => import('./components/pages/FindAllReceivedRequests'));
const FindAllGivenConsents = React.lazy(() => import('./components/pages/FindAllGivenConsents'));
const ReadConsent = React.lazy(() => import('./components/pages/ReadConsent'));
const FindData = React.lazy(() => import('./components/pages/FindData'));
const FindAllMetadata = React.lazy(() => import ('./components/pages/FindAllMetadata'));
const NewPartnerRequest = React.lazy(() => import('./components/pages/NewPartnerRequest'));
const FindAllGivenRequests = React.lazy(() => import('./components/pages/FindAllGivenRequests'));
const FindAllReceivedConsents = React.lazy(() => import('./components/pages/FindAllReceivedConsents'));
const Metadata = React.lazy(() => import('./components/data/Metadata'));
function App() {
return (
<BrowserRouter>
<Layout>
<Suspense fallback= { <div className = { classes.centered }> <LoadingSpinner /> </div> } >
<Switch>
<Route path ='/' exact>
<Dashboard />
<FindData />
</Route>
<Route path= '/new-data' exact>
<NewData />
</Route>
<Route path= '/metadata' exact>
<FindAllMetadata />
</Route>
<Route path = '/data'>
<Metadata />
</Route>
<Route path= '/request' exact>
<RequestMenu />
<FindAllReceivedRequests />
<section style = {{ marginTop: '5rem',
}}>
<FindAllGivenConsents />
</section>
</Route>
<Route path= '/givenrequest' exact>
<RequestMenu />
<FindAllGivenRequests />
<section style = {{ marginTop: '5rem',
}}>
<FindAllReceivedConsents />
</section>
</Route>
<Route path= '/transfer-data' exact>
<ReadConsent />
</Route>
<Route path= '/partner-request' exact>
<NewPartnerRequest />
</Route>
<Route path= '*'>
<NotFound />
</Route>
</Switch>
</Suspense>
</Layout>
<Footer />
</BrowserRouter>
);
}
export default App;
Here is my FindAllMetadata.js where I am fetching allMetadata ( it is working ):
import React, { useState, useEffect, useMemo } from 'react';
import AllMetadataTable from '../data/AllMetadataTable';
import LoadingSpinner from '../UI/LoadingSpinner';
import styles from '../UI/Messages.module.css';
import styled from '../style/Form.module.css';
import { readAllMetadata } from '../lib/api';
const FindAllMetadata = () => {
const [allMetadata, setAllMetadata] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
const [enteredMetadataFilter, setEnteredMetadataFilter] = useState('all');
// When page is loaded immediately fetch (and display) all metadata (only running the effect when enteredMetadataFilter changes)
useEffect(() => {
// fetch metadata by entered request filter (all or my)
const readAllMetadataHandler = async () => {
setIsLoading(true);
setError(null);
try {
const loadedAllMetadata = await readAllMetadata(enteredMetadataFilter);
setAllMetadata(loadedAllMetadata);
} catch (error) {
setError(error);
}
setIsLoading(false);
};
readAllMetadataHandler();
}, [enteredMetadataFilter]);
// display fetched content
const content = useMemo(() => {
if (error) {
return <div className={styles.negative}> { error.message } </div>;
} else if (isLoading) {
return <section style = {{margin : '1rem 17rem' }} ><LoadingSpinner /> </section>;
} else {
return (
<AllMetadataTable allMetadata = { allMetadata }
metadataFilter = { enteredMetadataFilter }
issLoading = { isLoading }
/>
);
}
}, [isLoading, error, allMetadata, enteredMetadataFilter]);
return (
<>
{/** pick filter for displaying metadata */}
{!isLoading &&
<section style= {{ marginLeft : '-10rem'}} >
<select className={styled.selectControl}
onChange={ event => {
setEnteredMetadataFilter(event.target.value);
}} >
<option value='' disabled style={{ color: '#cccccc' }} > Choose an option
</option>
<option value = 'all'> All Metadata </option>
<option value = 'my'> My Data </option>
</select>
</section>
}
<section>
{/**display content by status: error, loading, allmetadata, mymetadata */}
{ content }
</section>
</>
)
}
export default FindAllMetadata;
Here is my AllMetadataTable.js component where I am displaying fetched metadata in a table ( it is working and when I click + button it is redirecting me to correct URL ) :
import React from 'react';
import { Table, Button } from 'semantic-ui-react';
import "semantic-ui-css/components/table.min.css";
//import Metadata from './Metadata';
import classes from '../style/Form.module.css';
import Time from '../time/time';
import { useHistory } from 'react-router-dom';
import Metadata from './Metadata';
const AllMetadataTable = ({ allMetadata, metadataFilter, issLoading }) => {
const history = useHistory();
// sorted by time created - newest first
const allMetadataSorted = [...allMetadata].sort((a, b) => {
return new Date(b.TimestampCreated) - new Date(a.TimestampCreated);
});
// open details page for wanted metadata
const openDetailsPage = (key) => {
history.push({
pathname: 'data',
search: `?id=${allMetadata[key].DObjectId}`
})
};
return (
<>
{!issLoading &&
<Table celled fixed singleLine
style={{
width : '60rem',
marginLeft: '-10rem',
}} >
<Table.Header>
<Table.Row>
<Table.HeaderCell>Creator</Table.HeaderCell>
<Table.HeaderCell>Host</Table.HeaderCell>
<Table.HeaderCell>Domain</Table.HeaderCell>
<Table.HeaderCell>Format</Table.HeaderCell>
<Table.HeaderCell>Time Created</Table.HeaderCell>
<Table.HeaderCell
style={{
width : '4rem',
}}>Details</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{allMetadataSorted.map((metadata) => (
<React.Fragment key={metadata.key}>
<Table.Row>
<Table.Cell>{metadata.OrgIdCreator}</Table.Cell>
<Table.Cell>{metadata.OrgIdHost}</Table.Cell>
<Table.Cell>{metadata.TagsDomain}</Table.Cell>
<Table.Cell>{metadata.DataFormatId}</Table.Cell>
<Table.Cell>{Time(metadata.TimestampCreated)}</Table.Cell>
<Table.Cell>
{/** open/close metadata */}
<Button className={classes.uichange}
style ={{
border: 'none',
borderRadius: '0px',
color: 'white',
cursor: 'pointer',
backgroundColor: '#19a47c',
margin: '0 1rem',
fontSize : 22 }}
onClick={() => openDetailsPage(metadata.key) }>
+
</Button>
</Table.Cell>
</Table.Row>
</React.Fragment>
))}
</Table.Body>
</Table>
}
</>
);
};
export default AllMetadataTable;`
Here is my Metadata.js component which I wanna show in a new page when + button in a table is clicked ( id I am getting is corrrect and it is displaying in a list correctly but all other fields are empty; how can I access other fields and display them ?) :
`import React from 'react';
import classes from '../style/SingleData.module.css';
import list from '../style/List.module.css';
import { Button } from 'semantic-ui-react';
//import styles from '../UI/Messages.module.css';
import LoadingSpinner from '../UI/LoadingSpinner';
import Time from '../time/time';
import { useLocation } from 'react-router-dom';
/** import ORG_NAME */
const ORG = process.env.REACT_APP_ORG_NAME;
const Metadata = (props) => {
const { search } = useLocation();
const id = new URLSearchParams(search).get('id');
console.log(id);
// close metadata
const stopReadingDataHandler = () => {
props.onClose()
};
return (
<>
<ul className={list.tableList} >
<li style={{ borderRadius: '0px' }} className={classes.data}>
<strong> Data Id: </strong> {id}
</li>
<li style={{ borderRadius: '0px' }} className={classes.data}>
<strong> Doc Type Code: </strong> {props.DocTypeCode}
</li>
<li style={{ borderRadius: '0px' }} className={classes.data}>
<strong> Data Format: </strong> {props.DataFormatId}
</li>
<li style={{ borderRadius: '0px' }} className={classes.data}>
<strong>Creator: </strong> {props.OrgIdCreator}
</li>
<li style={{ borderRadius: '0px' }} className={classes.data}>
<strong> Host: </strong> {props.OrgIdHost}
</li>
<li style={{ borderRadius: '0px' }} className={classes.data}>
<strong>Tags Content: </strong> {props.TagsContent}
</li>
<li style={{ borderRadius: '0px' }} className={classes.data}>
<strong>Domain: </strong> {props.TagsDomain}
</li>
<li style={{ borderRadius: '0px' }} className={classes.data}>
<strong>Time Created: </strong> {Time(props.TimestampCreated)}
</li>
<li style={{ borderRadius: '0px' }} className={classes.data}>
<strong>Time Updated: </strong> {Time(props.TimestampUpdated)}
</li>
{ /** display Cancel button if you are Creator or Host */}
{!props.isLoading && (props.OrgIdCreator === ORG || props.OrgIdHost === ORG) && props.transferCheckStatus === false ?
<div style={{ justifyContent: 'flex-start' }}
className={classes.Form__actions}>
<Button className={classes.uichangedelete}
style ={{
border: 'none',
borderRadius: '3px',
color: 'white',
cursor: 'pointer',
backgroundColor: 'red',
margin: '10px',
fontSize : 22 }}
type= 'button'
content='Cancel'
onClick={ stopReadingDataHandler }
/>
</div>
: null }
{/** display loading spinner if loading */}
{props.isLoading && <LoadingSpinner />}
</ul>
</>
);
};
export default Metadata;
I tried using props inside FindAllMetadata, inside AllMetadataTable; I created other page Details.js in same folder as FindAllMetadata ( pages folder ) ; I tried useHistory, useLocation, useParams etc. `
In your openDetailsPage function you are passing only the id to the Metadata component:
const openDetailsPage = (key) => {
history.push({
pathname: 'data',
search: ?id=${allMetadata[key].DObjectId}
})
};
You are using history.push method with the following parameters:
pathname => which is '/data/ for Metadata component
search => query to url (NOTE: Here you are passing only the id)
Try adding:
state => an object to pass to the Metadata components:
history.push({
pathname: 'data',
search: `?id=${allMetadata[key].DObjectId}`,
state: {item: allMetadata[key]}
})
Then access it on the Metadata component by using:
props.location.state.item
I want to build a car list app that will show a list of cars, and when I click on a car's details button, it will route it to another component/page that will show the details of cars.
I can get the key (vin for me) but I want to get the details of the car for each key(vin) on this page.It's like http://localhost:3000/cars/WAUZZZ4G6FN052847= key. So when a car's key will show up, all details will come due to their key number. Thank you.
index.html
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import { BrowserRouter, Routes, Route, Outlet, } from "react-router-dom";
import DummyComponent from "./components/DummyComponent";
import CarDetails from "./pages/CarDetails";
import { Home } from "#mui/icons-material";
import Cars from "./pages/Cars";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<BrowserRouter>
<Routes>
<Route path="/" element={<App />} />
<Route path="/cars" element={<Outlet />} >
<Route path="list" element={<Cars />} />
<Route path=":vin" element={<CarDetails />} />
</Route>
<Route path="Home" element={<Home />} />
<Route path="DummyComponent" element={<DummyComponent />} />
</Routes>
</BrowserRouter>
);
Cars.js
import axios from "axios";
import React, { useEffect, useState } from "react";
import List from '#mui/material/List';
import ListItem from '#mui/material/ListItem';
import ListItemButton from '#mui/material/ListItemButton';
import ListItemText from '#mui/material/ListItemText';
import { Link } from "react-router-dom";
const App = () => {
const [cars, setCars] = useState([])
const getCarData = async () => {
try {
const data = await axios.get("https://react-challenge-api.azurewebsites.net/vehicles")
setCars(data.data)
}
catch (e) {
console.log(e)
}
}
useEffect(() => {
getCarData()
}, [])
return (
<div className="App">
<List sx={{ width: '100%', maxWidth: 600, bgcolor: 'background.paper' }}>
{cars.map((car) => (
<ListItemButton key={car.vin}>
<ListItem
key={car.vin}
disableGutters
secondaryAction={
<ListItemButton >
<Link to={`/cars/${car.vin}`}>details</Link>
</ListItemButton>
}
>
<ListItemText key={car.vin} primary={car.model_variant} />
</ListItem>
</ListItemButton>
))
}
</List >
</div >
);
};
export default App;
CarDetails.js (I want to show each data in this component, I used params but I don't know how to get data due to params.
import { useParams } from "react-router-dom";
const CarDetails = () => {
let params = useParams();
return (
<>
<h1>car</h1>
<ul>
this is your {params.vin}
</ul>
</>
)
}
export default CarDetails;
I would suggest moving the car data fetching into a layout route and pass the cars state down in an Outlet context.
Example:
Cars - Handles fetching the car data and passes the cars state along to nested routes via the Outlet component's context.
const Cars = () => {
const [cars, setCars] = useState([]);
const getCarData = async () => {
try {
const data = await axios.get(
"https://react-challenge-api.azurewebsites.net/vehicles"
);
setCars(data.data);
} catch (e) {
console.log(e);
}
};
useEffect(() => {
getCarData();
}, []);
return <Outlet context={{ cars }} />;
};
CarList - Reads the cars state from the Outlet context and renders the list.
const CarList = () => {
const { cars } = useOutletContext();
return (
<List sx={{ width: "100%", maxWidth: 600, bgcolor: "background.paper" }}>
{cars.map((car) => (
<ListItemButton key={car.vin}>
<ListItem
key={car.vin}
disableGutters
secondaryAction={
<ListItemButton>
<Link to={`/cars/${car.vin}`}>details</Link>
</ListItemButton>
}
>
<ListItemText key={car.vin} primary={car.model_variant} />
</ListItem>
</ListItemButton>
))}
</List>
);
};
CarDetails - Takes the vin route path param and the cars array from the outlet context and searches for a matching car object.
const CarDetails = () => {
const { vin } = useParams();
const { cars } = useOutletContext();
const car = cars.find((car) => car.vin === vin);
if (!car) {
return "No car matches this VIN";
}
return (
<>
<h1>{car.model_variant}</h1>
<ul>
<li>Body: {car.body_type}</li>
<li>Doors: {car.doors}</li>
<li>Fuel: {car.fuel_type}</li>
<li>VIN: {car.vin}</li>
<li>Registration #: {car.regno}</li>
<li>Transmission: {car.transmission_type}</li>
</ul>
</>
);
};
Routes
<Routes>
<Route path="/" element={<App />} />
<Route path="/cars" element={<Cars />}> // <-- layout route renders outlet
<Route path="list" element={<CarList />} />
<Route path=":vin" element={<CarDetails />} />
</Route>
<Route path="Home" element={<Home />} />
<Route path="DummyComponent" element={<DummyComponent />} />
</Routes>
I have this serch.js file. When I search and click on the li in the search result, I want to get redirected to <InnerDetail /> but the URL doesn't change or I don't get redirected to this page
but manualy if I type in the URL localhost/detiled/8 I am redirected to to <InnerDetail /> with id as 8
import React from "react";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faSearch } from "#fortawesome/free-solid-svg-icons";
import { useState, useEffect } from "react";
import axios from "axios";
import { Link } from "react-router-dom";
import { useHistory } from "react-router-dom";
const initialState = {
idaddProducts: "",
};
const Searchclients = () => {
const history = useHistory();
const [showResults, setShowResults] = React.useState(true);
const [poName, pnName] = React.useState(initialState);
const [showSerch, setShowSerch] = React.useState([]);
const [detail, setDetail] = useState(false);
const [inputValue, setInputValue] = React.useState("");
const [filteredSuggestions, setFilteredSuggestions] = React.useState([]);
const [selectedSuggestion, setSelectedSuggestion] = React.useState(0);
const [displaySuggestions, setDisplaySuggestions] = React.useState(false);
const suggestions = [];
showSerch.forEach(function (data) {
suggestions.push(data);
});
const onChange = (event) => {
const value = event.target.value;
setInputValue(value);
setShowResults(false);
const filteredSuggestions = suggestions.filter(
(suggestion) =>
suggestion.firstname
.toString()
.toLowerCase()
.includes(value.toLowerCase()) ||
suggestion.id.toString().toLowerCase().includes(value.toLowerCase())
);
setFilteredSuggestions(filteredSuggestions);
setDisplaySuggestions(true);
};
const onSelectSuggestion = (index) => {
setSelectedSuggestion(index);
setInputValue(filteredSuggestions[index]);
setFilteredSuggestions([]);
setDisplaySuggestions(false);
};
const SuggestionsList = (props) => {
// console.log(props);
const {
suggestions,
inputValue,
onSelectSuggestion,
displaySuggestions,
selectedSuggestion,
} = props;
if (inputValue && displaySuggestions) {
if (suggestions.length > 0) {
return (
<ul className="suggestions-list" style={styles.ulstyle}>
{suggestions.map((suggestion, index) => {
// console.log(suggestions);
const isSelected = selectedSuggestion === index;
const classname = `suggestion ${isSelected ? "selected" : ""}`;
return (
<Link to={`/detiled/${suggestion.id}`}> //this link dont work
<li
style={styles.listyle}
// onMouseOver={{ background: "yellow" }}
key={index}
className={classname}
>
{suggestion.firstname}
</li>
</Link>
);
})}
</ul>
);
} else {
return <div>No suggestions available...</div>;
}
}
return <></>;
};
useEffect(() => {
axios
.get("all-doctors-list/")
.then((res) => {
const data = res.data;
// pnName(data.data);
// var stringdata = data;
setShowSerch(data);
//console.log(stringdata);
});
// setShowSerch(data);
}, []);
return (
<>
<div className="note-container" style={styles.card}>
<div style={styles.inner}>
<p style={{ textAlign: "left" }}>Search Doctors</p>
<form className="search-form" style={{}}>
{showResults ? (
<FontAwesomeIcon
style={{ marginRight: "-23px" }}
icon={faSearch}
/>
) : null}
<input
onChange={onChange}
value={inputValue}
style={styles.input}
type="Search"
/>
<SuggestionsList
inputValue={inputValue}
selectedSuggestion={selectedSuggestion}
onSelectSuggestion={onSelectSuggestion}
displaySuggestions={displaySuggestions}
suggestions={filteredSuggestions}
/>
</form>
</div>
</div>
</>
);
};
export default Searchclients;
Navigator.js
import React from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import InnerDetail from "./client/doctor/components/innerto_detail.js";
class Navigator extends React.Component {
render() {
return (
<Router>
<div>
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/detiled/:id">
<InnerDetail />
</Route>
</Switch>
</div>
</Router>
);
}
}
function Home() {
return (
<div style={{ paddingTop: "20%", textAlign: "center" }}>
<h1>Home</h1>
</div>
);
}
export default Navigator;
when i hover on the<li> the bottom of the browser the path is showing currectly
i think the problem is here i tried another links here nothing is working here
<Link to={`/detiled/${suggestion.id}`}> //this link dont work
<li
style={styles.listyle}
// onMouseOver={{ background: "yellow" }}
key={index}
className={classname}
>
{suggestion.firstname}
</li>
</Link>
i tryed another link under the flowing code
a button called hello and that works i think the probelm is with the return
<SuggestionsList
inputValue={inputValue}
selectedSuggestion={selectedSuggestion}
onSelectSuggestion={onSelectSuggestion}
displaySuggestions={displaySuggestions}
suggestions={filteredSuggestions}
/>
<Link
to={{
pathname: `/detiled/5`,
}}
>
<button>hello</button>
</Link>
try using
<Route path="/detiled/:id" component={InnerDetail} />
instead of
<Route path="/detiled/:id">
<InnerDetail />`
in Navigator.js
and
Route exact path="/">
<Home />
did you created any Home component, and not imported that in Navigator.js
<Link to={"/detiled/"+ suggestion.id}><li>...</li></Link>
this worked for me
I want to access component according to the id of the product.
Here is my Code.
List.jsx
import { Switch, Route, Redirect, withRouter } from "react-router-dom";
import CategoryList from "./CategoryList.jsx";
import ProductList from "./ProductList.jsx";
import React from "react";
import axios from "axios";
const CategoryWithId = ({ match }) => {
console.log("Category", match.params.listId);
<ProductList listId={match.params.listId}></ProductList>;
};
function List() {
const [state, setState] = React.useState([]);
const getList = () => {
axios.get("https://api.growcify.com/dev/category/list").then((res) => {
console.log(res.data);
setState(res.data);
});
};
React.useEffect(() => {
getList();
}, []);
return (
<div className="App">
<Switch>
<Route path="/" component={() => <CategoryList state={state} />} />
<Route path="/product/:listId" component={CategoryWithId} />
<Redirect to="/" />
</Switch>
</div>
);
}
export default withRouter(List);
CategoryList.jsx
import React from "react";
import { Link } from "react-router-dom";
import Grid from "#material-ui/core/Grid";
import Paper from "#material-ui/core/Paper";
import { makeStyles } from "#material-ui/core/styles";
import { Typography } from "#material-ui/core";
const ShowCategory = (props) => {
const classes = useStyles();
const { list } = props;
return (
<Link to={`/product/${list._id}`}>
<Paper className={classes.paper} key={list._id}>
<p style={{ position: "relative", top: "40%" }}>{list.name}</p>
</Paper>
</Link>
);
};
function CategoryList(props) {
const { state } = props;
const classes = useStyles();
return (
<>
<div className={classes.header}>
<Typography variant="h3" style={{ padding: "10px" }}>
List of Categories
</Typography>
</div>
<div className={classes.container}>
<Grid container className={classes.root} spacing={2}>
<Grid item xs={12}>
<Grid container spacing={2}>
{state.map((list, index) => (
<ShowCategory list={list}></ShowCategory>
))}
</Grid>
</Grid>
</Grid>
</div>
</>
);
}
export default CategoryList;
I want that on clicking on Particular category, i should be redirect to /produce/listId.
In the url section, I can see the url with listId but I'm not getting redirected to the desired page related to that url.
Use exact keyword!
<Route exact path="/" component={() => <CategoryList state={state} />} />