I am trying to navigate from a List of annotations to an concrete Annotation using a Link from React Router. But It didn't render the component.
So, I have an AnnotationList in where I have all the annotations. Each annotation have a "". In my Container.js I have declared the Route like this:
<Route path="/annotationList/annView/:id" component={annView} />
And in the same Container I have the component annView:
const annView = ({ match }) => {
const { id } = match.params;
console.log(id);
return(
<AnnotationView id={id} />
)
}
When I click on the Link, the URL change to the correct one, but it didn't render anything. What Am I doing wrong?
I paste the complete code of these two js files if it helps.
Container.js
import React from 'react';
import { Route, Switch, withRouter, Link } from "react-router-dom";
import ProductList from './List/ProductList';
import CustomersList from './List/CustomersList';
import AppointmentsList from './List/AppointmentsList';
import AnnotationList from './List/AnnotationList';
import AddProduct from './Post/AddProduct';
import '../styles/App.css';
import AnnotationView from './Item/AnnotationView';
function Container({ location }) {
return (
<div>
<Switch location={location}>
<Route path="/productsList" component={ProductList} />
<Route path="/customersList" component={CustomersList} />
<Route path="/appointmentsList" component={AppointmentsList} />
<Route path="/annotationList" component={AnnotationList} />
<Route path="/annotationList/annView/:id" component={annView} />
<Route path="/addProduct" component={AddProduct} />
</Switch>
</div>
);
}
const annView = ({ match }) => {
const { id } = match.params;
console.log(id);
return(
<AnnotationView id={id} />
)
}
export default withRouter(Container);
import React, { Component } from 'react';
import { Table } from 'reactstrap';
import { Link } from 'react-router-dom';
import AnnotationView from '../Item/AnnotationView';
class AnnotationList extends Component {
constructor(props) {
super(props);
this.state = {
annotations: [],
isLoading: false
}
}
componentDidMount() {
this.setState({isLoading: true});
fetch('http://localhost:8080/annotations')
.then(response => response.json())
.then(data => this.setState({annotations: data, isLoading: false}));
}
render() {
const { annotations, isLoading } = this.state;
if(isLoading) {
return <p>Loading...</p>;
}
return(
<div>
<h2>Anotaciones</h2>
<div className="form-group">
<input type="date"/>
</div>
<Table>
{annotations.map((ann) =>
<div>
<tr>
<Link to={`/annotationsList/annView/${ann.id}`}>
<th>{ann.name}</th>
</Link>
</tr>
<tr>
<td>{ann.text}</td>
</tr>
</div>
)}
</Table>
</div>
)
}
}
export default AnnotationList;
Thanks in advance.
You have a typo in your Link component.
You're trying to match this route
<Route path="/annotationList/annView/:id" component={annView} />
but your Link has annotations with an s
/annotationsList/annView/anythingHere
You have to change your Link Component to this:
<Link to={`/annotationList/annView/${ann.id}`}>
<th>{ann.name}</th>
</Link>
Note: I would still recommend that every time you have similar routes you use exact on at least 1 of them to let React Router know how to differentiate between the routes.
function Container({ location }) {
return (
<div>
<Switch location={location}>
<Route path="/productsList" component={ProductList} />
<Route path="/customersList" component={CustomersList} />
<Route path="/appointmentsList" component={AppointmentsList} />
<Route exact path="/annotationList" component={AnnotationList} />
<Route path="/annotationList/annView/:id" component={annView} />
<Route path="/addProduct" component={AddProduct} />
</Switch>
</div>
);
}
Related
Actually, I am trying to render the DishDetail component, there is an error like "Warning: Functions are not valid as a React child." Therefore, I cannot render the DishDetail Component under "/menu/:dishId".
Anyone can help me? I have tried googling it. The "DishDetailComponent" and "MainComponent" are below respectively. Thanks a lot!
The MainComponent:
import React, { Component, useContext} from 'react';
import Home from './HomeComponent';
import Menu from './MenuComponent';
import Contact from "./ContactComponent";
import DishDetail from './DishdetailComponent';
import Header from './HeaderComponent';
import About from './AboutComponent';
import Footer from './FooterComponent';
import { Routes, Route, Navigate, useParams} from 'react-router-dom';
import {connect} from 'react-redux';
const mapStateToProps = (state) => {
return {
dishes: state.dishes,
comments: state.comments,
leaders: state.leaders,
promotions: state.promotions
}}
class Main extends Component{
constructor(props) {
super(props);}
render() {
const HomePage =() => {
return(
<Home
dish={this.props.dishes.filter((dish) => dish.featured)[0]}
promotion={this.props.promotions.filter((promo) => promo.featured)[0]}
leader={this.props.leaders.filter((leader) => leader.featured)[0]}
/>
);
}
const DishWithId = ({match}) => {
return(
<DishDetail dish={this.props.dishes.filter((dish) => dish.id === parseInt(match.params.dishId, 10))[0]}
comments={this.props.comments.filter((comment) => comment.dishId === parseInt(match.params.dishId, 10))}
/>
);
};
return (
<div>
<Header/>
<Routes>
<Route path ="/home" element={HomePage()} />
<Route exact path="/menu" element={<Menu dishes={this.props.dishes} />} />
<Route path='/menu/:dishId' element={<DishWithId />} />
<Route exact path="/contactus" element={<Contact />} />
<Route exact path="/aboutus" element={<About leaders={this.props.leaders}/>} />
<Route path="*" element={<Navigate replace to="/home" />} />
</Routes>
<Footer />
</div>
);}}
export default connect(mapStateToProps)(Main);
The DishDetailComponent:
import React from 'react';
import { Card, CardImg, CardText, CardBody, CardTitle , Breadcrumb, BreadcrumbItem} from 'reactstrap';
import { Link } from 'react-router-dom';
function RenderDish({dish}) {
console.log('Menu Component render is invoked')
return(
<div className="col-12 col-md-5 m-1">
<Card>
<CardImg width="100%" object src={dish.image} alt={dish.name} />
<CardBody>
<CardTitle>{dish.name}</CardTitle>
<CardText>{dish.description}</CardText>
</CardBody>
</Card>
</div>
);
}
function RenderComments({comments}) {
const comment = comments.map((comment)=> {
return (
<div key={comment.id} className="col-12 col-md-5 m-1">
<p>{comment.comment}</p>
<p>--{comment.author} , {new Intl.DateTimeFormat('en-US', {year: 'numeric', month: 'long', day: '2-digit' }).format(new Date(comment.date))}</p>
</div>
)
});
return(
<div className="col-12 col-md-5 m-1">
<h4>Comment</h4>
{comment}
</div>
);
}
const DishDetail = (props) => {
if (props.dish != null) {
return (
<div className='container'>
<div className='row'>
<Breadcrumb>
<BreadcrumbItem><Link to="/menu">Menu</Link></BreadcrumbItem>
<BreadcrumbItem active>{props.dish.name}</BreadcrumbItem>
</Breadcrumb>
<div className='col-12'>
<h3>{props.dish.name}</h3>
<hr />
</div>
</div>
<div className='row'>
<div className="col-12 col-md-5 m-1">
<RenderDish dish={props.dish} />
</div>
<div className="col-12 col-md-5 m-1">
<RenderComments comments={props.comments} />
</div>
</div>
</div>
);
}
else {
return (
<div></div>
)
}
} export default DishDetail;
HomePage and DishWithId are function components, so this is simply undefined in both cases. Also, you should completely avoid declaring React components within other React components. They should be declared outside on their own. The filtering should occur in the parent component and the filtered result should be passed as a prop to the routed component.
Example:
import React, { Component, useContext } from 'react';
import Home from './HomeComponent';
import Menu from './MenuComponent';
import Contact from "./ContactComponent";
import DishDetail from './DishdetailComponent';
import Header from './HeaderComponent';
import About from './AboutComponent';
import Footer from './FooterComponent';
import { Routes, Route, Navigate, useParams } from 'react-router-dom';
import { connect } from 'react-redux';
const DishWithId = ({ dishes, comments }) => {
const { dishId } = useParams();
return (
<DishDetail
dish={dishes.find((dish) => String(dish.id) === dishId)}
comments={comments.find((comment) => String(comment.dishId) === dishId)}
/>
);
};
class Main extends Component{
constructor(props) {
super(props);
}
render() {
return (
<div>
<Header/>
<Routes>
<Route
path="/"
element={(
<Home
dish={this.props.dishes.find((dish) => dish.featured)}
promotion={this.props.promotions.find((promo) => promo.featured)}
leader={this.props.leaders.find((leader) => leader.featured)}
/>
)}
/>
<Route
path="/menu"
element={<Menu dishes={this.props.dishes} />}
/>
<Route
path='/menu/:dishId'
element={(
<DishWithId
dishes={this.props.dishes}
comments={this.props.comments}
/>
)}
/>
<Route path="/contactus" element={<Contact />} />
<Route path="/aboutus" element={<About leaders={this.props.leaders}/>} />
<Route path="*" element={<Navigate replace to="/home" />} />
</Routes>
<Footer />
</div>
);
}
}
const mapStateToProps = (state) => ({
dishes: state.dishes,
comments: state.comments,
leaders: state.leaders,
promotions: state.promotions
});
export default connect(mapStateToProps)(Main);
It would be better to ditch the connect HOC and use the useSelector hook directly in the components that need to access the state. This avoid the odd props drilling issue.
Example:
Home
import React from 'react';
import { useSelector } from 'react-redux';
const Home = () => {
const dishes = useSelector(state => state.dishes);
const leaders = useSelector(state => state.leaders);
const promotions = useSelector(state => state.promotions);
const dish = dishes.find((dish) => dish.featured);
const promotion = promotions.find((promo) => promo.featured);
const leader = leaders.find((leader) => leader.featured);
return .....;
};
DishDetail
import React from 'react';
import { useParams } from 'react-router-dom';
import { useSelector } from 'react-redux';
const DishDetail = () => {
const { dishId } = useParams();
const dishes = useSelector(state => state.dishes);
const comments = useSelector(state => state.comments);
const dish = dishes.find((dish) => String(dish.id) === dishId);
const comments = comments.find((comment) => String(comment.dishId) === dishId);
return <DishDetail {...{ comments, dish }} />;
};
... etc for other routed components ...
App
import React, { Component, useContext } from 'react';
import Home from './HomeComponent';
import Menu from './MenuComponent';
import Contact from "./ContactComponent";
import DishDetail from './DishdetailComponent';
import Header from './HeaderComponent';
import About from './AboutComponent';
import Footer from './FooterComponent';
import { Routes, Route, Navigate } from 'react-router-dom';
class Main extends Component{
constructor(props) {
super(props);
}
render() {
return (
<div>
<Header/>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/menu" element={<Menu />}/>
<Route path='/menu/:dishId' element={<DishWithId />}/>
<Route path="/contactus" element={<Contact />} />
<Route path="/aboutus" element={<About />} />
<Route path="*" element={<Navigate replace to="/home" />} />
</Routes>
<Footer />
</div>
);
}
}
export default Main;
I would like to have a button to redirect to homepage on 404 and errorBoundary pages.
I am getting history object and url changes on button click.
However, it does not reload homepage.
Link & useHistory works perfectly on other pages except these two !
tried Router & BrowserRouter both from 'react-router-dom', but none of them worked.
Any advice ? Thank you !!
versions
react 17.0.2
react-dom 17.0.2
react-router-dom 5.2.0
routes.js
import React, { lazy, Suspense } from 'react';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import { createBrowserHistory } from 'history';
const history = createBrowserHistory();
const routes = () => (
<Router history={history}>
<ErrorBoundary>
<Suspense fallback={<Loading />}>
<App />
<Switch>
<Route path="/login" component={Login} />
<Route exact path={['/', '/reserves']} component={ReservesMain} />
<PrivateRoute path="/reserve/:id" component={ReserveMain} />
<PrivateRoute path="/managers" component={ManagersMain} />
<PrivateRoute path="/manager/:id" component={ManagerMain} />
<Route component={NotFound} />
</Switch>
</Suspense>
</ErrorBoundary>
</Router>
);
notFound.js
import { useHistory, Link } from 'react-router-dom';
import Button from '../components/shared/button';
...
export default function NotFound() {
const history = useHistory();
const onRedirect = () => history.push('/');
return (
<div>
<h1>404. Not Found. </h1>
<p>Oops! Something's missing...</p>
<p>The page does not exist or is unavailable.</p>
<Link to={'/'}>redirect button</Link>
<Button name="Go Home" onClick={onRedirect} />
</div>
);
}
errorBoundary.js
import React from 'react';
import { connect } from 'react-redux';
import { useHistory } from 'react-router-dom';
import Button from '../components/shared/button';
const BadRequest = ({ status }) => [
<h1 key="be-1">
{status ? <span>{status}.</span> : null} This page isn't working.
</h1>,
<h2 key="be-2">Your client has issued a malformed or illegal request.</h2>,
<h2 key="be-3">If the problem continues, contact the engineer department.</h2>,
];
const SystemError = ({ status }) => [
<h1 key="se-1">
{status ? <span>{status}.</span> : null} Something went wrong.
</h1>,
<h2 key="se-2">Oops! We're experiencing an internal Server Error.</h2>,
<h2 key="se-3">Please try again later.</h2>,
];
export const ErrorComponents = ({ status }) => {
const history = useHistory();
const onRedirect = () => history.push('/');
return (
<>
<div style={rootStyle}>
<div style={innerStyles}>
{status === 400 ? <BadRequest status={status} /> : <SystemError status={status} />}
<Button name="Go Home" onClick={onRedirect} />
</div>
</>
);
};
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { error: null };
}
static getDerivedStateFromError(error) {
return { error };
}
componentDidCatch(error, info) {
if (process.env.NODE_ENV === 'development') {
console.log(`error : ${error.name}, message: ${error.message || 'null'}`);
console.log(`info : ${JSON.stringify(info)}`);
}
}
render() {
if (this.state.error) {
return <ErrorComponents status={this.props.asyncApi.status} />;
}
return this.props.children;
}
}
const mapStateToProps = (state) => ({
asyncApi: state.asyncApi,
});
export default connect(mapStateToProps, null)(ErrorBoundary);
I have the following NavLink in my React Sidebar component that successfully links to an add-folder new page; however, I'm unable to render an AddFolder form on this new page (I've successfully tested that the AddFolder form renders in the Sidebar component, but that's not what I want). I've tried numerous things to resolve this, including using "component={AddFolder}", "render={={() => { return }}". Most recently I tried adding an onClick which doesn't work either.
<section className="AddFolder_Section">
<NavLink
className="AddFolder_Button"
tag={Link}
to={'/add-folder'}
type='button'
onClick={() => this.AddFolder}
>
Add Folder
</NavLink>
</section>
Are there pointers that you can give me on what might work here? Thank you.
Here are the entire contents of my sidebar.js file:
import React, { Component } from 'react'
import { NavLink, Route, Link } from 'react-router-dom'
import NotesContext from './notesContext'
import AddFolder from './addFolder'
import './sidebar.css'
class Sidebar extends Component {
static contextType = NotesContext
render() {
let notesContext = this.context
const selectedFolder = this.props.match.params.folder_id || {}
return (
<section className="Sidebar">
{notesContext.folders.map(folder => {
return(
<section key={folder.id} className={(selectedFolder && selectedFolder === folder.id)? "SelectedFolder": "Folder"}>
<NavLink className="Folder_Link"
tag='button'
role='link'
to={'/folder/' + folder.id}>
{folder.name}
</NavLink>
</section>
)
})}
<section className="AddFolder_Section">
<NavLink
className="AddFolder_Button"
tag={Link}
to={'/add-folder'}
type='button'
onClick={() => this.AddFolder}
>
Add Folder
</NavLink>
<Route
path='/add-folder'
render={(props) =>
<AddFolder
{...props}
/>
}
/>
</section>
</section>
);
}
}
export default Sidebar;
As I understand your question: AddFolder is your "form" component.
You need to first import your AddFolder component on the parent component that you want to render the form on.
import AddFolder from '../somePath/AddFolder';
Then on your render() function, place the <Route> and simply render the AddFolder component when you are on the add-folder path. Pass props if needed.
<Route
path='/add-folder'
render={(props) =>
<AddFolder
{...props}
/>
}
/>
It turns out I had a typo in the Route defined in my App.js. I needed 'add-folder' instead of 'add_folder'. Now the Route in App works with the NavLink in Sidebar. Yeah!
Here is the App.js:
import React, { Component } from 'react'
import { Route, Switch } from 'react-router-dom'
import Header from './header.js'
import Main from './main.js'
import MainDetail from './mainDetail.js'
import Sidebar from './sidebar.js'
import SidebarDetail from './sidebarDetail.js'
import NotesContext from './notesContext'
import './App.css'
import config from './config'
import AddFolder from './addFolder'
import AddNote from './addNote'
class App extends Component {
state = {
folders: [],
notes: [],
error: null,
};
setData = (notes, folders) => {
this.setState({
notes,
folders,
error: null
})
}
deleteNote = note_id => {
fetch(config.API_ENDPOINT + "/notes/" + note_id, {
method: 'DELETE',
headers: {
'content-type': 'application/json'
}
})
.then(response => response.json())
.then(result => {
const newNotes = this.state.notes.filter(note => note.id !== note_id)
this.setState({
notes: newNotes
})
})
}
addFolder = (name, id) => {
this.setState({
folders: [...this.state.folders, {name, id}]
})
}
componentDidMount() {
Promise.all([
fetch(config.API_ENDPOINT + "/notes"),
fetch(config.API_ENDPOINT + "/folders")
])
.then(responses => {
return Promise.all(responses.map(function (response) {
return response.json();
}));
})
.then(results => {
this.setData(results[0], results[1])
})
.catch(error => this.setState({ error }))
}
render() {
const contextValue = {
notes: this.state.notes,
folders: this.state.folders,
deleteNote: this.deleteNote,
addFolder: this.addFolder
}
//console.log(this.state.notes)
return (
<NotesContext.Provider value={contextValue}>
<section className="App">
<Switch>
<Route
exact path="/"
component={Header}
/>
<Route
path="/folder/:folder_id"
component={Header}
/>
<Route
path="/note/:note_id"
component={Header}
/>
</Switch>
<section className="Section_Sidebar">
<Switch>
<Route
exact path="/"
component={Sidebar}
/>
<Route
path="/folder/:folder_id"
component={Sidebar}
/>
<Route
path="/note/:note_id"
component={SidebarDetail}
/>
</Switch>
</section>
<section className="Section_Main">
<Switch>
<Route
exact path="/"
component={Main}
/>
<Route
path="/folder/:folder_id"
component={Main}
/>
<Route
path="/note/:note_id"
component={MainDetail}
/>
<Route
path="/add-folder"
component={AddFolder}
/>
<Route
path="/add-note"
component={AddNote}
/>
</Switch>
</section>
</section>
</NotesContext.Provider>
);
}
}
export default App;
This is the updated Sidebar.js:
import React, { Component } from 'react'
import { NavLink, Route, Link } from 'react-router-dom'
import NotesContext from './notesContext'
import AddFolder from './addFolder'
import './sidebar.css'
class Sidebar extends Component {
static contextType = NotesContext
render() {
let notesContext = this.context
const selectedFolder = this.props.match.params.folder_id || {}
return (
<section className="Sidebar">
{notesContext.folders.map(folder => {
return(
<section key={folder.id} className={(selectedFolder && selectedFolder === folder.id)? "SelectedFolder": "Folder"}>
<NavLink className="Folder_Link"
tag='button'
role='link'
to={'/folder/' + folder.id}>
{folder.name}
</NavLink>
</section>
)
})}
<section className="AddFolder_Section">
<NavLink
className="AddFolder_Button"
tag={Link}
to={'/add-folder'}
type='button'
>
Add Folder
</NavLink>
</section>
</section>
);
}
}
export default Sidebar;
Here my code:
this app root react
import React, { Suspense, lazy } from 'react';
import { Link, BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import './App.css';
const Home = lazy(() => import('./screen/home/Home'));
const About = lazy(() => import('./screen/about/About'));
function App() {
return (
<Router>
<Suspense fallback={<div>Loading...</div>}>
<Switch>
<Route exact path="/" strict component={ Home }/>
<Route path="/about" strict component={ About }/>
<Route path="*" strict component={ () => " 404 Page Not Found " } />
</Switch>
</Suspense>
</Router>
);
}
export default App;
my render component return like this
render() {
return(
<input type="checkbox"
id="a"
name="cheker"
value="a"
/>
<label for="a">A</label>
<input type="checkbox"
id="b"
name="cheker"
value="b"
/>
<label for="b">A</label>
<input type="checkbox"
id="c"
name="cheker"
value="c"
/>
<label for="c">A</label>
)
}
}
my component and handlecheked like this
export default class Home extends Component{
constructor(props){
super(props);
this.state = {
postData: [], // i use this for json data
checked: false,
isLoading: true
}
}
handlecheked= () => {
// this for handle if 2 checked selecbox will go to about page
}
const rootEl = document.getElementById('root');
ReactDOM.render(
<BrowserRouter>
<Switch>
<Route exact path="/">
<MasterPage />
</Route>
<Route exact path="/details/:id" >
<DetailsPage />
</Route>
</Switch>
</BrowserRouter>,
rootEl
);
I am trying access the id in the DetailsPage component but it is not being accessible. I tried
<DetailsPage foo={this.props}/>
to pass parameters to the DetailsPage, but in vain.
export default class DetailsPage extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="page">
<Header />
<div id="mainContentContainer" >
</div>
</div>
);
}
}
So any idea how to pass the ID on to the DetailsPage ?
I used this to access the ID in my component:
<Route path="/details/:id" component={DetailsPage}/>
And in the detail component:
export default class DetailsPage extends Component {
render() {
return(
<div>
<h2>{this.props.match.params.id}</h2>
</div>
)
}
}
This will render any ID inside an h2, hope that helps someone.
If you want to pass props to a component inside a route, the simplest way is by utilizing the render, like this:
<Route exact path="/details/:id" render={(props) => <DetailsPage globalStore={globalStore} {...props} /> } />
You can access the props inside the DetailPage using:
this.props.match
this.props.globalStore
The {...props} is needed to pass the original Route's props, otherwise you will only get this.props.globalStore inside the DetailPage.
Since react-router v5.1 with hooks:
import { useParams } from 'react-router';
export default function DetailsPage() {
const { id } = useParams();
}
See https://reacttraining.com/blog/react-router-v5-1/
Use render method:
<Route exact path="/details/:id" render={(props) => (
<DetailsPage id={props.match.params.id}/>
)} />
And you should be able to access the id using:
this.props.id
Inside the DetailsPage component
In addition to Alexander Lunas answer ...
If you want to add more than one argument just use:
<Route path="/details/:id/:title" component={DetailsPage}/>
export default class DetailsPage extends Component {
render() {
return(
<div>
<h2>{this.props.match.params.id}</h2>
<h3>{this.props.match.params.title}</h3>
</div>
)
}
}
Use the component:
<Route exact path="/details/:id" component={DetailsPage} />
And you should be able to access the id using:
this.props.match.params.id
Inside the DetailsPage component
This is for react-router-dom v6 (I highly suggest using functional components for this)
It's somewhat painful for react-router-dom to keep changing syntax and rules. But here goes nothing.
You can use both useParams and useSelector to solve this
import { useParams } from 'react-router';
import { useSelector } from 'react-redux';
const Component = () => {
const { id } = useParams(); //returns the :id
const page = useSelector((state) => state.something[id]); //returns state of the page
return <div>Page Detail</div>;
}
export default Component;
BUT, the problem persist when you also have an action creator and you want to pass it as a props in connect function
export const connect(mapStateToProps, mapDispatchToProps)(Component)
since we are using useParams, it won't be passed to mapStateToProps that we created
const mapStateToProps = (state, ownProps) => {
console.log(ownProps) //wont recognize :id
//hence
return {
someReducers: state.someReducers[id] //would return an error: 'id' is not defined
};
};
on the other hand, you can't entirely ignore the connect function since you need mapDispatchToProps to work with your component.
The workaround to this is to create a Higher Order Component withRouter function yourself. This was a deprecated react-router-dom helper.
//make this
import { useParams, useLocation, useNavigate } from 'react-router';
import { connect } from 'react-redux';
import { yourActionCreator } from '../actionCreator';
const withRouter = (Child) => {
return (props) => {
const location = useLocation();
const navigation = useNavigate();
const params = useParams();
return (
<Child
{...props}
params={params}
navigate={navigate}
location={location}
/>
);
};
};
const Component = () => {
// your component...
return <div> Page Detail </div>
};
export mapStateToProps = (state, ownProps) => {
console.log(ownProps) // would contain the :id params
return {
//something
}
};
const mapDispatchToProps = {
yourActionCreator
}
export withRouter(connect(mapStateToProps, mapDispatchToProps)(Component));
Here's typescript version. works on "react-router-dom": "^4.3.1"
export const AppRouter: React.StatelessComponent = () => {
return (
<BrowserRouter>
<Switch>
<Route exact path="/problem/:problemId" render={props => <ProblemPage {...props.match.params} />} />
<Route path="/" exact component={App} />
</Switch>
</BrowserRouter>
);
};
and component
export class ProblemPage extends React.Component<ProblemRouteTokens> {
public render(): JSX.Element {
return <div>{this.props.problemId}</div>;
}
}
where ProblemRouteTokens
export interface ProblemRouteTokens {
problemId: string; }
Another solution is to use a state and lifecycle hooks in the routed component and a search statement in the to property of the <Link /> component. The search parameters can later be accessed via new URLSearchParams();
<Link
key={id}
to={{
pathname: this.props.match.url + '/' + foo,
search: '?foo=' + foo
}} />
<Route path="/details/:foo" component={DetailsPage}/>
export default class DetailsPage extends Component {
state = {
foo: ''
}
componentDidMount () {
this.parseQueryParams();
}
componentDidUpdate() {
this.parseQueryParams();
}
parseQueryParams () {
const query = new URLSearchParams(this.props.location.search);
for (let param of query.entries()) {
if (this.state.foo!== param[1]) {
this.setState({foo: param[1]});
}
}
}
render() {
return(
<div>
<h2>{this.state.foo}</h2>
</div>
)
}
}
FOR version 6 ( 2022 )
Note: using useParams you can easily get your params in your component.
look at the example below
import React from "react";
import { BrowserRouter as Router, Routes, Route, Link } from "react-router-dom";
import Home from "./compo/home";
import About from "./compo/about";
import Login from "./compo/login";
import "./styles.css";
const App = () => {
return (
<Router>
<div className="container">
<Link to="/home">Home</Link>
<Link to="/about">About</Link>
<Link to="/login">Login</Link>
</div>
<Routes>
<Route path="/home" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/login" element={<Login />} />
<Route path="/login/:name" element={<Login />} />
</Routes>
</Router>
);
};
export default App;
Login Component
import { useParams } from "react-router-dom";
const Login = () => {
let { name } = useParams();
return <h1>i am {name ? <b>{name}</b> : "login"}</h1>;
};
export default Login;
if you are using class component, you are most likely to use GSerjo suggestion. Pass in the params via <Route> props to your target component:
exact path="/problem/:problemId" render={props => <ProblemPage {...props.match.params} />}
In the latest version of (react-router-dom#6.3.0), you can do it like this:
<Route path="path" element={<YourComponent type="simple" />} />
Here, type is the input passed to YourComponent
I was working on react-router-dom version 6.3.0 and above solution didn't resolve my problem. Then I use something like this and it worked:
<Route exact path='/payment-status/:userId/:orderId' element={<PaymentStatus/>}/>
And on PaymentStatus.js page I did like this:
import { useParams } from 'react-router-dom'
export const PaymentStatus = () => {
let {userId, orderId}=useParams()
return (
<div>
<h2>order ID : {orderId}</h2>
<h2>user ID : {userId}</h2>
</div>
)
}
It worked for me. I hope it may help someone. Thanks!
try this.
<Route exact path="/details/:id" render={(props)=>{return(
<DetailsPage id={props.match.params.id}/>)
}} />
In details page try this...
this.props.id
Simple example with Class, HoC and Router v5
package.json
"react-router-dom": "5.3.1",
"react-router": "5.3.1",
"#types/react-router-dom": "5.3.3",
// YourComponent.tsx
import React from 'react';
import { RouteComponentProps, withRouter } from 'react-router';
export interface PathParams {
id: string;
}
export interface Props extends RouteComponentProps<PathParams> {}
export interface State {}
class YourComponent extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {};
console.log(props.match.params) // { id: 1 }
// TypeScript completions
console.log(props.match.params.id) // 1
}
render() {
return <></>;
}
}
export default withRouter(YourComponent);
// App.tsx
import './App.css';
import React from 'react';
import { Route, Switch, Router } from 'react-router-dom';
import YourComponent from './YourComponent';
function App(): JSX.Element {
return (
<Router>
<Switch>
<Route
path="/details/:id"
component={() => <YourComponent />}
/>
</Switch>
</Router>
);
}
export default App;