React-router: Redux router state to have same shape as react-router props

Created on 7 Aug 2017  路  6Comments  路  Source: ReactTraining/react-router

I'm working on an app that has a page route that looks like:

/:USERNAME/photos/:PHOTO_ID

Before, I was using react-router-dom without redux, so when I needed to access those params I would get them from this.props.match which has a rich mapping of the current route.

{
  isExact: true,
  params: {
    USERNAME: "paul"
    PHOTO_ID: "123"
  },
  path: "/:USERNAME/photos/:PHOTO_ID",
  url: "/paul/photos/123",
}

The issue is if other parts that the app need these variables it's not clear how to share them. _Enter Redux_. However, using the react-router-redux binding I see that the state that's returned looks like:

{
  hash: "",
  key: "057zsw",
  pathname: "/paul/photos/123",
  search: "",
  state: undefined
}

There should be more info in the state similar to the match object passed down to components by react-router-dom. I _could_ make a custom function to parse the parameters but that would be duplicating the work that's already being done by react-router-dom. Thoughts?

Most helpful comment

A workaround I've used is to create a selector (ala reselect) and pull out the 'params' via a regex. This smells bad / feels hacky to me though. Having multiple Route components per 'page' is a cool idea, but in practice >95% of the time I just want a single match per route. YMMV 馃檭

import { createSelector } from 'reselect'

const crewDataSelector = state => state.crews.data;
const pathnameSelector = state => state.router.location.pathname;

const crewIdSlector = createSelector(
  pathnameSelector,
  pathname => {
    const matches = pathname.match(/crew\/(\d+)/)
    if (matches) return matches[1];
  }
)

const currentCrewSelector = createSelector(
  crewIdSlector,
  crewDataSelector,
  (crewId, crewData) => crewData.crewId
)

export default currentCrewSelector

All 6 comments

This isn't possible because the match is relative to the Route that matched on it. You can have more than one Route in your tree that matches, but you cannot have more than one Redux store. Unfortunately, it's fundamentally incompatible. Your best bet is to pass down props.

A workaround I've used is to create a selector (ala reselect) and pull out the 'params' via a regex. This smells bad / feels hacky to me though. Having multiple Route components per 'page' is a cool idea, but in practice >95% of the time I just want a single match per route. YMMV 馃檭

import { createSelector } from 'reselect'

const crewDataSelector = state => state.crews.data;
const pathnameSelector = state => state.router.location.pathname;

const crewIdSlector = createSelector(
  pathnameSelector,
  pathname => {
    const matches = pathname.match(/crew\/(\d+)/)
    if (matches) return matches[1];
  }
)

const currentCrewSelector = createSelector(
  crewIdSlector,
  crewDataSelector,
  (crewId, crewData) => crewData.crewId
)

export default currentCrewSelector

@tylercrosse this is a great solution, I'm going to give this a shot.

So it won't never be possible to obtain params in @@router/LOCATION_CHANGE action?
馃

I'm not a react-router / redux expert but, maybe that the mounted route could update the routing state to give params and then trigger @@router/LOCATION_CHANGE ?

I came across this same scenario. Has anyone been able to figure out a more elegant solution?

I ended wrapping my component with withRouter prior to connecting it to redux.

export default connect(mapStateToProps, mapDispatchToProps)(withRouter(MyComponent));

This adds a bunch of unused props to my component but works for now.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

winkler1 picture winkler1  路  3Comments

stnwk picture stnwk  路  3Comments

ArthurRougier picture ArthurRougier  路  3Comments

nicolashery picture nicolashery  路  3Comments

maier-stefan picture maier-stefan  路  3Comments