Test Case
https://codesandbox.io/s/rr00y9w2wm
Steps to reproduce
- Click on Topics
- Click on Rendering with React
OR
Expected Behavior
match.params.topicId
should be identical from both the parent Topics component should be the same asmatch.params.topicId
when accessed within the Topic component
Actual Behavior
match.params.topicId
when accessed within the Topic component is undefinedmatch.params.topicId
when accessed within the Topics component is rendering
I understand from this closed issue that this is not necessarily a bug.
This requirement is super common among users who want to create a run in the mill web application where a component Topics
at a parent level needs to access the match.params.paramId where paramId
is a URL param that matches a nested (child) component Topic
:
const Topic = ({ match }) => (
<div>
<h2>Topic ID param from Topic Components</h2>
<h3>{match.params.topicId}</h3>
</div>
);
const Topics = ({ match }) => (
<div>
<h2>Topics</h2>
<h3>{match.params.topicId || "undefined"}</h3>
<Route path={`${match.url}/:topicId`} component={Topic} />
...
</div>
);
In a generic sense, Topics
could be a Drawer or Navigation Menu component and Topic
could be any child component, like it is in the application I'm developing. The child component has it's own :topicId
param which has it's own (let's say) <Route path="sections/:sectionId" component={Section} />
Route/Component.
Even more painful, the Navigation Menu needn't have a one-to-one relationship with the component tree. Sometimes the items at the root level of the menu (say Topics
, Sections
etc.) might correspond to a nested structure (Sections
is only rendered under a Topic, /topics/:topicId/sections/:sectionId
though it has its own normalized list that is available to the user under the title Sections in the Navigation Bar).
Therefore, when Sections is clicked, it should be highlighted, and not both Sections and Topics.
With the sectionId
or sections
path unavailable to the Navigation Bar component which is at the Root level of the application, it becomes necessary to write hacks like this for such a commonplace use case.
I am not an expert at all at React Router, so if anyone can venture a proper elegant solution to this use case, I would consider this to be a fruitful endeavor. And by elegant, I mean
- Uses
match
and nothistory.location.pathname
- Does not involve hacky approaches like manually parsing the
window.location.xxx
- Doesn't use
this.props.location.pathname
- Does not use third party libraries like
path-to-regexp
- Does not use query params
Other hacks/partial solutions/related questions:
TIA!
React-router
doesn't give you the match params of any of the matched children Route , rather it gives you the params based on the current match. So if you have your Routes setup likeand in
Topics
component you have a Route likeNow if your url is
/topic/topic1
which matched the inner Route but for the Topics component, the matched Route is still,/topic
and hence has no params in it, which makes sense.If you want to fetch params of the children Route matched in the topics component, you would need to make use of
matchPath
utility provided by React-router and test against the child route whose params you want to obtainEDIT:
One method to get all the params at any level is to make use of context and update the params as and when they match in the context Provider.
You would need to create a wrapper around Route for it to work correctly, A typical example would look like
RouteWrapper.jsx
ParamsProvider.jsx
Index.js
Working DEMO
Try utilizing query parameters
?
to allow the parent and child to access the current selectedtopic
. Unfortunately, you will need to use the module qs becausereact-router-dom
doesn't automatically parse queries (react-router v3 does).Working example: https://codesandbox.io/s/my1ljx40r9
URL is structured like a concatenated string:
topic?topic=props-v-state
Then you would add to the query with
&
:/topics/topic?topic=optimization&category=pure-components&subcategory=shouldComponentUpdate
✔ Uses match for Route URL handling
✔ Doesn't use
this.props.location.pathname
(usesthis.props.location.search
)✔ Uses
qs
to parselocation.search
✔ Does not involve hacky approaches
Topics.js
Another approach would be to create a
HOC
that stores params tostate
and children update the parent'sstate
when its params have changed.URL is structured like a folder tree:
/topics/rendering/optimization/pure-components/shouldComponentUpdate
Working example: https://codesandbox.io/s/9joknpm9jy
✔ Uses match for Route URL handling
✔ Doesn't use
this.props.location.pathname
✔ Uses lodash for object to object comparison
✔ Does not involve hacky approaches
Topics.js
NestedRoutes.js