I'm trying to pass a function to a a React Router but it gives me an error despite several adjustments. I tried putting the function in the render(), added this before params props, but nothing seems to be working. How do you you pass a function to selective return between a Route and a Redirect tag?
import React, { Component } from 'react';
import { Switch, Route, Redirect } from 'react-router-dom';
import DogList from './DogList';
import DogDetails from './DogDetails';
class Routes extends Component {
constructor(props) {
super(props);
this.getDog = this.getDog.bind(this);
}
getDog() {
let name = props.match.params.name;
let currDog = this.props.dogs.find(
dog => dog.name.toLowerCase() === name.toLowerCase()
);
if(currDog != undefined) {
return <DogDetails {...props} dog={currDog} /> ;
} else {
return <Redirect to="/dogs" />
}
}
render() {
return(
<Switch>
<Route exact path='/dogs' render= {() => <DogList dogs={this.props.dogs} />} />
<Route exact path='/dogs/:name' render={(props) => {this.getDog()}} />
<Redirect to='/dogs' />
</Switch>
);
}
}
export default Routes;
I recommend you to seperate your components because there might be so many routes, so, you might not be able to manage them in one component.
Anyway, in your case please try sending props as a parameter to your function.
You should wrap your switches with BrowserRouter.
import React, { Component } from 'react';
import { Switch, Route, Redirect, BrowserRouter as Router } from 'react-router-dom';
import DogList from './DogList';
import DogDetails from './DogDetails';
class Routes extends Component {
constructor(props) {
super(props);
this.getDog = this.getDog.bind(this);
}
getDog(props) {
const { dogs } = this.props;
let name = props.match.params.name;
let currDog = dogs.find(
dog => dog.name.toLowerCase() === name.toLowerCase()
);
if(currDog != undefined) {
return <DogDetails {...props} dog={currDog} /> ;
} else {
return <Redirect to="/dogs" />
}
}
render() {
const { dogs } = this.props;
return(
<Router>
<Switch>
<Route exact path='/dogs' render= {() => <DogList dogs={dogs} />} />
<Route exact path='/dogs/:name' render={(props) => this.getDog(props)} />
<Redirect to='/dogs' />
</Switch>
</Router>
);
}
}
Keep in mind this react router documentation. It is a good guide to your example; https://reactrouter.com/web/guides/quick-start
A complete example is here; https://codesandbox.io/s/sleepy-ishizaka-n0433?file=/src/App.js
Use this.props not only props
let name = this.props.match.params.name;
import React, { Component } from "react";
import { BrowserRouter, Route, Switch, Redirect } from "react-router-dom";
import { library } from "#fortawesome/fontawesome-svg-core";
import {
faHome,
faClock,
faTasks,
faStickyNote,
faCalendarWeek
} from "#fortawesome/free-solid-svg-icons";
import { connect } from "react-redux";
import store from "./store";
import { loadUser } from "./actions/authActions";
import Home from "./Home";
import SideNav from "./Components/SideNav";
import Recent from "./Components/Recent";
import TopBar from "./Components/TopBar";
import AddNote from "./AddNote";
import LogIn from "./Components/LogIn/LogIn.js";
import Register from "./Components/Register/Register";
import ToDo from "./Components/ToDo/ToDo";
import { timingSafeEqual } from "crypto";
library.add(faHome, faClock, faTasks, faStickyNote, faCalendarWeek);
class App extends Component {
componentDidMount() {
store.dispatch(loadUser());
}
componentDidUpdate(prevProps) {
if(this.props != this.prevProps) {
console.log("hello")
}
}
LogInContainer = () => {
return <Route path="/login" component={LogIn} />;
};
RegisterContainer = () => {
return <Route path="/register" component={Register} />;
};
DefaultContainer = () => {
return (
<div className="app_container">
<SideNav />
<TopBar />
<Route exact path="/" component={Home} />
<Route path="/recent" component={Recent} />
<Route path="/AddNote" component={AddNote} />
<Route path="/ToDo" component={ToDo} />
</div>
);
};
// Check for authenticaition
AuthRoute = ({ component: Component, props, ...rest }) => {
return (
<Route
{...rest}
render={props => {
if (this.props.auth.isAuthenticated) {
return <Component {...props} />;
}
else {
return (
<Redirect
to={{
pathname: "/login",
state: { from: this.props.location }
}}
/>
);
}
}}
/>
);
};
render() {
return (
<BrowserRouter>
<Switch>
<Route exact path="/login" component={this.LogInContainer} />
<Route exact path="/register" component={this.RegisterContainer} />
<this.AuthRoute component={this.DefaultContainer} />
</Switch>
</BrowserRouter>
);
}
}
const mapStateToProps = (state) => ({
auth: state.auth
})
export default connect(mapStateToProps)(App);
How can app.js receive the new state from redux after logging in? The initial fetch it will get isAuthenticated = false. User then log ins but app.js isn't getting the new state. Am I implementing authenitcation wrong? comonentDidUpdate is throwing an error when trying to update props but feel like this is a bad way of doing it anyways
The App can't switch and render All the routes. It gets only one path (firstOne) and renders it.
import React, { Component } from "react";
import { Route, Switch } from "react-router-dom";
import { connect } from "react-redux";
class Board extends Component {
state = {
routes: []
};
showRoutes = routes =>
routes.length > 0 &&
routes.map((route, i) => (
<Route key={i} path={route.path} component={()=>"path"+i} />
));
render() {
const { routes } = this.props;
return (
<div>
<Switch>
<Route exact path="/" render={() => "start page"} />
{this.showRoutes(routes.routesApi)}
<Route path="/" render={() => "no such routes"} />
</Switch>
</div>
);
}
}
const mapStateToProps = state => ({
routes: state.routes
});
export default connect(mapStateToProps)(Board);
I have also used componentWillReceiveProps() as:
componentWillReceiveProps(NextProps) {
this.setState({ routes: NextProps.routes });
}
and switch to read data from state, but the result is the same.
Could you help me please to understand what is wrong?
In my react application I'm trying to redirect to dashboard after than login, but since than protected route name is not 'route', react router can't detect it as a route and just changes the URL.
I've been checked unprotected and simple routes such 'register' and that loaded like a charm on redirect process.
It seems react router 4 can't detect protected route as a route.
here are my routes in app.jsx:
const routes = (
<Router>
<div>
<Route exact path="/" component={Login}/>
<Route path="/login" component={Login}/>
<Route path="/register" component={Register}/>
<Route path="/resetpassword" component={ResetPassword}/>
<RestaurantAuthRoute path="/dashboard" component={RPDashboard}/>
<RestaurantAuthRoute path="/dashboard/addfood" component={RPAddFood}/>
</div>
</Router>
);
RestaurantAuthRoute.jsx:
var {connect} = require('react-redux');
var loginAuth = require('../api/loginAuth');
import {bindActionCreators} from 'redux';
import * as actions from 'actions';
import { Route, Redirect } from 'react-router-dom';
class RestaurantAuthRoute extends React.Component {
constructor(props){
super(props);
}
componentWillMount() {
var mytoken = localStorage.getItem('token');
var that = this;
loginAuth.restaurantAuthCheck(mytoken).then( function(res){
if(res.success == true){
that.props.actions.setRestaurantAuth(true);
}
else if(res.success == false){
that.props.actions.setRestaurantAuth(false);
}
});
}
render() {
if(this.props.restaurantLog.status == true || this.props.restaurantLog.status == false){
const { component: Component, restaurantLog, ...rest } = this.props;
return (
<Route {...rest} render={props => {
return restaurantLog.status
? <Component {...props} />
: <Redirect to="/login" />
}} />
)
}
else{
return(<p>wait</p>)
}
}
}
function mapStateToProps(state, ownProps){
return{
restaurantLog: state.logRestaurantState
}
}
function MapDispatchToProps(dispatch){
return{
actions: bindActionCreators(actions, dispatch)
}
}
export default connect(
mapStateToProps,
MapDispatchToProps
)(RestaurantAuthRoute);
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;