react-router: match.params returns empty even when URL contains params
Hi, I’m in the process of upgrading to react-router 4 from v2. I’m not sure if this is a bug or not, but I’m having a really hard time getting my Header component to show the correct route params object. It doesn’t seem like the match.params object is available globally like the params object was in react-router 2 and I’m not sure how to make it available to sibling components.
App.jsx (wrapped with ConnectedRouter)
-> contains Layout.jsx
const Layout = function(props) {
return (
<div className="layout" style={layout.base}>
<Header inlineStyles={layout.header} params={props.match.params} />
<Content inlineStyles={layout.content} />
<Footer inlineStyles={layout.footer} />
</div>
);
};
Layout.propTypes = {
match: PropTypes.object
};
export default Layout;
The Content component is where the bulk of my routes begin. When I try to access props.match inside of the Header component, I see this:
// Header.jsx
match: {
isExact: false,
params: {},
path: "/",
url: "/"
}
But when I access the props.match object from the nested component route, I get this:
// Content.jsx > Child > GrandChild
match: {
isExact: true,
params: {orgId: "1", projectId: "11", version: "1.0.0", modelId: "30"},
path: "/orgs/:orgId/projects/:projectId/version/:version/models/:modelId",
url: "/orgs/1/projects/11/version/1.0.0/models/30"
}
This is in the same render cycle.
- Is it expected that the match object is not global to the entire router?
- How do I access the same match object in my Header.jsx file as I have in my Content.jsx?
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Reactions: 27
- Comments: 25 (4 by maintainers)
Thank you @CaitlinWeb for the
matchPathsuggestion, that’s super helpful.For anyone else running into this problem, this was my solution:
And then you can use the
matchobject just like you would normally:To me, it is not clear why you would ever Not include all params in the match object, I really do not see a use case where you would not want all params to be listed (opposite argument). Because as mentioned by @goodbomb, you can always read the location URL in full.
I could give you an example for my use case, it involves layouts at the root of the Router, with dynamic and “static-ish” content, that still needs to change based on the route currently viewed. (And I now have to write some URL parsing algorithm).
I would consider it a total anti-pattern to ever have two matching ids with the same name like
/document/:docId/nested/:docId/nested2/:docId, where I can understand that you don’t want them to be overridden. Adding a check for that in everypathwould do the trick.I believe merging all match objects is the most intuitive way to go.
Edit: So for me, this is a bug, and is in the right place 😃
matchprop is<Route>relative (that is to say, inherited from the nearest parent<Route>)<Header>and<Content>components based on the matched route b) have a second set of routes inside of your<Header>to figure out which route matches c) use somecontext“magic” to get route components to report their params.I don’t actually know what you need the params in your header for, so I can’t really give a recommendation on which approach is “best”, but it’s probably either a or b.
Also, if you have any further usage questions, please use StackOverflow or Reactiflux since those communities are setup to answer questions. The issues section here is meant for bug reports.
With all due respect to the collaborators, I find it a bit strange that this issue has been closed. I am facing the same issue as well, and I find it really surprising that there is no universally accepted Design Pattern for handling this situation. The
matchParamsolution seems to be a hack at best. I really love React Router and it disappoints me that the issue was closed prematurely and without a proper solution. Because of this and because I am powerless to open this issue, I’m opening a new issue <fill issue link here> to address this issue.@pshrmn Your response that this is an inappropriate place to post this suggests that user input requesting clarification on whether behavior is either expected or a bug, is unwelcome. It also seems to indicate being very closed to input on how things ought to behave, or what users of this library would like to see. I don’t mean to be overly negative, especially because I love React Router and appreciate all the work on it. But this seems a little bit discouraging to the community of the library’s users, and rather dismissive of user concerns.
I also ran into this problem. My use case is exactly the same as goodbomb’s, I have
Breadcrumbsthat are in the parent component withRoutes as children, so it isn’t getting thematchI need.Good to know this was intentional. I guess I’m not the only one who didn’t pay close attention to the documentation:
So I’m going to try
matchPathwithin myBreadcrumbscomponent. Hope that helps others stumbling on this issue.Thanks for the response, @pshrmn. I have a custom Breadcrumb component that needs access to the global set of route params in order to render properly (swapping out the param numbers for their respective names). Having the params be relative is really counter-intuitive, especially since the location URL is available globally.
Guys, this seems way too hacky of an approach. What @Shaderpixel says, is actually true, but the solutions provided here, are too over-engineered, for something that should be a no-brainer, as having the params as a global configuration. React-router has some really weird idiosyncrasies.
In my case, It won’t work, because I have structured my app a bit differently than you did, I also thing that @pshrmn answer is very good, but I am using a second set of routes already and I am not planning to change the folder structure for that…
I don’t understand why you closed this issue. It is still happening.
The question on Stack Overflow is here: https://stackoverflow.com/questions/53997670/react-router-dom-url-gets-redirected-to-the-root-path . If someone can take a look, and help me out a little bit it would be great.
I agree with @Floriferous, it seems counter intuitive that your params wouldn’t be globally available. Would be very useful in a number of situations to have access to them.
I also find it totally counter-intuitive that match would not always show this. And I need it because I’m using a container component around my routes to log page views to a database. I wound up doing it with the container because I could find no good place to do it anywhere in the page component itself (constructor would only log a page view the first time a page was loaded or reloaded from the server, and componentDidMount would log every time the page rerendered, giving, for example, three page view records when really the user only viewed the page once). But, with the container, I can do it on the initial load and then check for a pathname change on properties change, and it behaves exactly as I want that way.
What I’m left with is being forced to re-invent the wheel myself and write my own code to parse out the requested pathname in a rather clunky way that assumes certain segments are parameters, which both duplicates logic and opens up lots of great opportunities for bad page view data to be recorded.
Thanks @spiritman110, didn’t know that API was exposed!
It’s still not an acceptable solution though, since you have to provide the path you want to match, when what’s asked here is that any matched param just matches everywhere. This is really inconvenient if you want it to work anywhere in a remotely scaleable manner
I wrote this completely over the top HOC that includes @spiritman110 suggestion.
Here’s how to use it:
Assuming all the routes with the
:userIdmatcher look like/user/userId23/whatever.This issue should be revisited.
@spiritman110 very helpful, saved me from apocalypse
Noticed that if
<Route>is written using the component prop<Route path="/view/:postId" component={Single} />, the params is available inside<Single>'s props.match vs<Route path="/view/:postId" render={() => <Single {...this.props} /> />.I like what @spiritman110 has suggested, but I also noticed that since match.params is available in the state of
<Route>is it also ok to pass it down to the child component via props? Are there any downsides to doing it via props?I’m new to React and can’t understand nothing of those “thumbs up” solutions comments above. It all seems way over-complicated for something so simple.
So I ended up using the DOM API
URLSearchParamsMy example query string is (for registration process):
And in the Registration component:
@lazarljubenovic The routing in react-router is hierarchical.
Each
<Route>evaluates the current location based on it’s own props and propagates the result down the tree via context.(Simplified syntax for demonstration purposes)
In the above example, the outer route has no knowledge of the inner route.
I don’t know exactly why the decision was made, but I think it’s to allow easy nested routing and a simple codebase. Supporting both nested (and dynamic) routing like that while also communicating the innermost routing info up the tree while keeping todays flexibility isn’t exactly trivial.
This issue is still happening and cost me a week of coding.
the same issue, I have dynamic routing and I can’t pass the same string to mathPath
Not sure whether this is helpful at all to people still seeing this issue, but thought we were having this issue and wrote in a work around but it turned out to be actually that we’d misspelt the word
exactasexcatin our<Route>component by accident.As soon as we spotted that and fixed it,
paramswas filled with the params we were passing.Yes the params are global in the browser, the url can be considered a global right? So i makes sense that it would be available in any
withRoutercomponent