118
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 ?

M T
  • 3,649
  • 4
  • 20
  • 27

12 Answers12

134

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.

Alexander Luna
  • 4,977
  • 4
  • 27
  • 35
117

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.

Win
  • 2,003
  • 2
  • 10
  • 14
  • 3
    But why would it not work the way I specified in the ask ? – M T Aug 30 '17 at 13:50
  • 4
    First, your code was wrong. Placing a component inside a Route like `` was not the same as ``. Your code rendered `DetailsPage` even if you visited '/'. Second, when you wrote ``, `this` referred to `null`. That's why it didn't work. – Win Aug 30 '17 at 14:13
  • This gives an error `Warning: [react-router] You cannot change ; it will be ignored` – Hareesh Apr 17 '18 at 10:19
  • Please open a new question and share your code there. – Win Apr 18 '18 at 07:16
  • Please refer to @Alexander Luna 's answer below for a simpler solution. – FedericoCapaldo Apr 23 '21 at 08:16
60

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/

user1338062
  • 10,623
  • 3
  • 64
  • 60
40

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

tsi
  • 1,204
  • 11
  • 10
Steven Daniel Anderson
  • 1,375
  • 1
  • 10
  • 17
14

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>
    )
  }
}
Manuel
  • 2,011
  • 4
  • 17
  • 33
12

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

Seraf
  • 779
  • 15
  • 34
Dekel
  • 57,326
  • 8
  • 92
  • 123
  • Interesting this works. However, why this was out separate because I wanted to pass a variable down to the component. ` ` Now passing the globalStore doesn't works. – M T Aug 26 '17 at 20:00
  • 1
    @Dekel I am trying to do the exact same thing, but I am getting an error. So, I have created a new question. link: https://stackoverflow.com/questions/52652521/react-router-passing-props-to-component I am trying to access the id in console as simple as that. – Arnab Oct 04 '18 at 18:01
  • Instead of 'component' on 'Route' if you use 'render' it will give good performance – Kodali444 Nov 20 '19 at 10:21
5

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; }

GSerjo
  • 4,690
  • 1
  • 32
  • 52
4

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>
        )
      }
    }
Manuel
  • 2,011
  • 4
  • 17
  • 33
3

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));
Joundill
  • 5,418
  • 11
  • 31
  • 47
diazlp
  • 163
  • 2
  • 6
2

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} />}
Matteus Barbosa
  • 1,987
  • 18
  • 18
0

try this.

<Route exact path="/details/:id" render={(props)=>{return(
    <DetailsPage id={props.match.params.id}/>)
}} />

In details page try this...

this.props.id
0

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;
Filip Seman
  • 748
  • 1
  • 9
  • 19