How to get parameters in parent route component
I am creating an application with React, React-Router (v5) and Redux and am wondering how to access the parameters of the current url in the parent route.
That my entry router.js
:
<Wrapper>
<Route exact path="/login" render={(props) => (
<LoginPage {...props} entryPath={this.entryPath} />
)} />
<Route exact path="/" component={UserIsAuthenticated(HomePage)} />
<Route exact path="/about" component={UserIsAuthenticated(AboutPage)} />
<Route path="/projects" component={UserIsAuthenticated(ProjectsPage)} />
</Wrapper>
And that's my component ProjectsPage
:
class ProjectsPage extends PureComponent {
componentDidMount() {
this.props.fetchProjectsRequest()
}
render() {
console.log(this.props.active)
if (this.props.loading) {
return <Loading />
} else {
return (
<Wrapper>
<Sidebar>
<ProjectList projects={this.props.projects} />
</Sidebar>
<Content>
<Route exact path="/projects" component={ProjectsDashboard} />
<Switch>
<Route exact path="/projects/new" component={ProjectNew} />
<Route exact path="/projects/:id/edit" component={ProjectEdit} />
<Route exact path="/projects/:id" component={ProjectPage} />
</Switch>
</Content>
</Wrapper>
)
}
}
}
const enhance = connect(
(state, props) => ({
active: props.match,
loading: projectSelectors.loading(state),
projects: projectSelectors.projects(state)
}),
{
fetchProjectsRequest
}
)
export default withRouter(enhance(ProjectsPage))
The problem is that the output console.log
in my method render
is equal {"path":"/projects","url":"/projects","isExact":false,"params":{}}
though the url http://localhost:3000/projects/14
.
I want to add a ID
prop to mine ProjectList
to highlight the currently selected project.
I could store the project ID in a store inside my component ProjectPage
, but I think this will be a little confusing, especially since the url has information in fact - so why would I write something in the store?
Another (bad?) Approach would be to parse the location object to get the ID yourself, but I think there is a way react-router
/ react-router-redux
get the parameters at this point that I missed.
source to share
@Kyle explained the problem very well from a technical point of view. I'll just focus on solving this problem.
You can use matchPath
to get the id
selected project.
matchPath - https://reacttraining.com/react-router/web/api/matchPath
This allows you to use the same matching code that is used outside of the normal render loop, such as collecting dependency data before rendering to the server.
The use in this case is very straight forward.
1 Use matchPath
// history is one of the props passed by react-router to component
// @link https://reacttraining.com/react-router/web/api/history
const match = matchPath(history.location.pathname, {
// You can share this string as a constant if you want
path: "/articles/:id"
});
let articleId;
// match can be null
if (match && match.params.id) {
articleId = match.params.id;
}
2 Use articleId
in render
{articleId && (
<h1>You selected article with id: {articleId}</h1>
)}
I am creating a simple demo that you can use to implement the same functionality in your project.
Demo: https://codesandbox.io/s/pQo6YMZop
I think this solution is quite elegant because we are using the official API react-router
which is also used for path mapping in the router. We're not using it window.location
here either , so testing / mocking will be easy if you export the original component as well.
source to share
TL; DR
React router match.params
will be an empty object if your property <Route />
path
doesn't include :params
.
Solve this use case: you can use let id = window.location.pathname.split("/").pop();
in your parent route component to get the id.
Detailed reason why not
If the path
prop provided in <Route />
doesn't have any parameters, for example /:id
, the responding router isn't going to do the parsing for you. If you look matchPath.js
at line # 56, you can start to see the construct being generated match
.
return {
path: path, // the path pattern used to match
url: path === '/' && url === '' ? '/' : url, // the matched portion of the URL
isExact: isExact, // whether or not we matched exactly
params: keys.reduce(function (memo, key, index) {
memo[key.name] = values[index];
return memo;
}, {})
};
Then we can look at line # 43, you can see what keys
comes from _compilePath.keys
. Then we can look at the function compilePath
and see what it uses pathToRegexp()
, which will use stringToRegexp()
, which will use tokensToRegExp()
, which will then mutate keys
on line # 355 with keys.push(token)
. When <Route />
params is irrelevant in its support path
, the function parse()
used in stringToRegexp()
will not return any tokens, and line # 355 will not even be reached, because a single token array will not contain any token objects.
So ... if yours <Route />
doesn't :params
, then the key value will be an empty array and you won't have any parameters on match
.
In conclusion, it looks like you will need to get the parameters yourself if your route doesn't account for them. You could use let id = window.location.pathname.split("/").pop()
.
source to share