router5 / redux-router5

Router5 integration with redux [MOVED]
86 stars 5 forks source link

Use of routeNodeSelector() with mapStateToProps() #14

Open tomzmtl opened 7 years ago

tomzmtl commented 7 years ago

Hi, I'm implementing the router in a standard react-redux app.

So far connecting component to router states was easy following the examples, for instance:

export default connect(routeNodeSelector('about'))(About);

but now I'd like to, in addition and the same component, retrieve a slice of the redux state using a classic mapStateToProps() function:

const mapStateToProps = state => ({
  progress: state.user.progress,
});

What would be the best to achieve this? The examples show no such use case.

Thank you again!

letam commented 7 years ago

You can do this if you're using the object rest spread transform:

const mapStateToProps = state => ({
  ...routeNodeSelector('about')(state),
  progress: state.user.progress,
});

Without the transform:

const mapStateToProps = state => (Object.assign({},
  routeNodeSelector('about')(state),
  {
    progress: state.user.progress,
  }
));

The very last example at the bottom of the README shows how this was reached.

troch commented 7 years ago

You can compose routeNodeSelector with your mapStateToProps. The proper way to use routeNodeSelector is as a thunk, otherwise memoization won't work properly.

const mapStateToProps = (state) => {
    const routeSelector = routeNodeSelector('about');

    return (state) => ({
        ...routeSelector(state),
        progress: state.user.progress
    });
};
cyberhck commented 6 years ago

hmm, I want to know one more thing:

const mapStateToProps = state => ({
  ...routeNodeSelector('user')(state),
  progress: state.user.progress,
});

what does the 'user' key do? I thought if I had user.list, user.edit etc etc, it'd give me only list, edit etc on route.name, but that doesn't seem to be the case, or am I doing it wrong? Or did I misunderstood routeNodeSelector?

troch commented 6 years ago

routeNodeSelector is a memoized selector which only returns a new value (routing state) if the provided node name (in your case 'user' is concerned by a route change.

Your code sample doesn't leverage memoization because it creates a new selector each time, so at a result it behaves like a normal selector. It should be:

const mapStateToProps = state => {
  const selector = routeNodeSelector('user')

  return state => ({
    ...selector(state),
    progress: state.user.progress
  })
})

Do you have more context around your use case?

cyberhck commented 6 years ago

ahh, got that part, as of now I'm having to do something like: const segment = route.name.split(".").length > 1 ? route.name.split(".")[1] : "index", I thought if I pass in user, it'd would return something like "create" instead of "user.create", but never mind :)

So I have to extract the routeNodeSelector and then it would use memoization? ahh, now I get it :smile: I didn't realize that before (actually didn't see it), it's a function which returns a function, but mine isn't. will do, thanks :)

troch commented 6 years ago

Also I imagine you have another route node selector (or component bound to the route) above your routeNodeSelector('user')?

cyberhck commented 6 years ago

yes, the index level, index has stuff like:

{
 user: UsersPage,
 settings: SettingsPage,
 about: AboutPage
}

and so on. On index page, I do routeNodeSelector("") and get the route, and I do split(".")[0], about page doesn't have subroutes, but user and settings page have list, and user page has edit and create page, which are subroutes of initial page.

On UsersPage I do routeNodeSelector("user") then do a split(".")[1] (after handling undefined cases), then render List, Edit or Create page depending on segment.