13

PrivateRoute available in Example https://reacttraining.com/react-router/web/example/auth-workflow is not working after connecting with Redux.

My PrivateRoute look almost same to the original version but only connected to Redux and used it instead of fakeAuth in the original example.

const PrivateRoute = ({ component: Component, auth, ...rest }) => (
  <Route
   {...rest}
   render={props =>
   auth.isAuthenticated
    ? <Component {...props} />
    : <Redirect to={{ pathname: "/login" }} />}
  />
);

 PrivateRoute.propTypes = {
  auth: PropTypes.object.isRequired,
  component: PropTypes.func.isRequired
 }

 const mapStateToProps = (state, ownProps) => {
  return {
     auth: state.auth
  }
};

export default connect(mapStateToProps)(PrivateRoute);

Usage and result:-

  1. NOT working but desired & expecting to work
    • <PrivateRoute path="/member" component={MemberPage} />
  2. working but NOT desired to used like this
    • <PrivateRoute path="/member" component={MemberPage} auth={auth} />
  3. working. JUST to work but NOT desired to used at all. An understanding from this point is that, if you connect original PrivateRoute to Redux you need to pass some additional props (any prop) to make PrivateRoute working otherwise it does not work. Anyone, please give some hint on this behavior. This is my main question
    • <PrivateRoute path="/member" component={MemberPage} anyprop={{a:1}} />
Premchandra Singh
  • 13,868
  • 4
  • 25
  • 37

3 Answers3

12

Complementary to @Tharaka answer you can pass {pure: false} to connect method as described in react-redux troubleshooting section.

React-redux makes shallow comparison of props in shouldComponentUpdate hook to avoid unnecessary re-renders. If context props changed (react-router) it doesn’t check for that and it assumes nothing has changed.

{ pure:false } simply disables this shallow comparison.

vsync
  • 87,559
  • 45
  • 247
  • 317
Tomasz Mularczyk
  • 27,156
  • 17
  • 99
  • 146
8

According to react-router documentation you may just wrap your connect function with withRouter:

// before
export default connect(mapStateToProps)(Something)

// after
import { withRouter } from 'react-router-dom'
export default withRouter(connect(mapStateToProps)(Something))

This worked for me.

Oleksii Trekhleb
  • 1,795
  • 16
  • 21
  • Brilliant! This was the simplest solution. I also removed the shouldComponentUpdate() function as well which worked for me – user2986096 Jun 12 '18 at 01:24
7

This is known issue in react-redux and you can read more about this here. The problem is connect HoC have implemented shouldComponentUpdate, so that wrapped component doesn't rerender if props aren't changed. Since react-router use context to pass route changes, the wrapped component doesn't rerender when routes change. But it seems they are going to remove shouldComponentUpdate in 5.1 version of react-redux. Currently, as a workaround, I pass a prop coming from Router like this.props.match.params to connected child component even though I don't use it in inside. But it will make rerendering the component each time when routes change.

Tharaka Wijebandara
  • 7,357
  • 1
  • 25
  • 44
  • Can you write me a working usage example of PrivateRoute or changes need to be done on PrivateRoute itself – Premchandra Singh May 10 '17 at 15:50
  • I can't think of any way to make PrivateRoute work as you expected in your 1 usage. My suggestion is that just go with usage 3 if it works for you until they fix this issue. Or you can pass `params` to PrivateRoute like this. `` – Tharaka Wijebandara May 10 '17 at 16:14
  • also as one more workaround i use **forceUpdate()** right after the change history – Alexey Nikonov Jun 07 '19 at 08:51
  • **update:** i found out that parent components using redux must also be connected to *withRouter* therefrom there is no need use *forceUpdate()* – Alexey Nikonov Jun 07 '19 at 08:57