Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get params in parent route component

I'm building an app with React, React-Router (v5) and Redux and wonder how to access the params of the current URL in a parent route.

That's my entry, the 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 ProjectsPage component:

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 console.log output in my render method is {"path":"/projects","url":"/projects","isExact":false,"params":{}} although the URL is http://localhost:3000/projects/14.

I want to add an ID prop to my ProjectList to highlight the currently selected project.

I could save the ID of the project in a store inside my ProjectPage component, but I think this would be a bit confusing, especially because the URL has the information actually – so why should I write something in the store?

Another (bad?) approach would be to parse the location object the get the ID by myself, but I think there is a react-router/react-router-redux way to get the params at this point that I've overlooked.

like image 698
Slevin Avatar asked Aug 03 '17 14:08

Slevin


1 Answers

@Kyle explained issue very well from technical perspective. I will just focus on solution to that problem.

You can use matchPath to get id of selected project.

matchPath - https://reacttraining.com/react-router/web/api/matchPath

This lets you use the same matching code that uses except outside of the normal render cycle, like gathering up data dependencies before rendering on the server.

Usage 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 build a simple demo which you can use to implement the same functionality in your project.

Demo: https://codesandbox.io/s/pQo6YMZop

I think that this solution is quite elegant because we use official react-router API which is also used for path matching in router. We also don't use window.location here so testing / mocking will be easy if you export also raw component.

like image 164
Dawid Karabin Avatar answered Sep 21 '22 06:09

Dawid Karabin