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.

+3


source to share


2 answers


@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.

+6


source


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()

.

+1


source







All Articles