14

I've set up browserHistory on a router with this (react-router 2.0):

import { browserHistory } from 'react-router'

function requireAuth(nextState, replace) {
    if (!services.auth.loggedIn()) {
        replace({
            pathname: '/login',
            state: { nextPathname: nextState.location.pathname }
        })
    }
}

export default (store) => (
  <Router history={browserHistory}>
    <Route path='/' component={AppLayout}>
      <Route path="login" component={LoginContainer} />
      <Route path="map" component={MapContainer} onEnter={requireAuth} />
    </Route>
  </Router>
);

I'm then trying to use browserHistory in react-router to programmatically route to a new page from a view, ala:

 import { browserHistory } from 'react-router'

 ...

 browserHistory.push('/map');

This changes the URL to /map but does not render the components in that route. What am I doing wrong?

outside2344
  • 2,075
  • 2
  • 29
  • 52
  • Can I see your `requireAuth` handler, also the map view? – Chris Frank Feb 20 '16 at 19:06
  • ok - added. note: the same thing happens (/map is not rendered) if I don't have the onEnter attribute. – outside2344 Feb 20 '16 at 19:09
  • yes - the browserHistory.push('/map') is called - and I see the URL change, but the new Route component (MapContainer) is not rendered. Finally, for completeness, if I turn off auth, and go to /map directly, it renders correctly. – outside2344 Feb 20 '16 at 19:10
  • Are you using a server behind this app? Nginx, or something else? These need to be set up to use a catch all route and send everything to index.html. As I'm sure you probably know. Since you have experience with ember. – Chris Frank Feb 20 '16 at 19:12
  • Yup - I'm using this application seed in particular: https://github.com/davezuko/react-redux-starter-kit – outside2344 Feb 20 '16 at 20:04
  • I'm having this exact problem also. Very keen to see if/how you overcome it. I'll post if I find a solution. – Magento Guy Feb 22 '16 at 10:02

2 Answers2

11

As mentioned in my comment I was having this same problem, but I've found a way to make it work.

What's happening here is your Route is changing, but your AppLayout component isn't actually updating it's state automatically. The Router doesn't seem to automatically force a state change on the component. Basically, this.state.children on your AppLayout is not being updated with the new children.

The solution that I found (and, full disclosure, I don't know if this is how you're supposed to achieve this, or if it's best practice) is to use the componentWillReceiveProps function and update this.state.children with the children from the new props:

componentWillReceiveProps(nextProps) {
    this.setState({
        children: nextProps.children
    });
}

Hope this helps!

Magento Guy
  • 2,493
  • 1
  • 16
  • 13
  • 2
    This seems like the best way but this is the only answer I can find on the internet for this problem, coupled with really mediocre documentation on github. Could you please expand on this in any way possible. I'm not sure exactly where I should be using componentWillReceiveProps, in my top level component container my router? I am using var children = React.Children.map(children, function (child) { return React.cloneElement(child, { user: user, key: key }); }); to clone my props, within a pure js function. – mibbit Nov 13 '16 at 11:33
2

An alternative solution that allows the router to navigate to another section of your app without having to force an update via lifecycle method is using context router. All you need is to declare a contextType in your component (omitting router instantiation and other code for brevity):

class MyComp extends Component {
  static contextTypes = {
    router: PropTypes.object
  }

  handleClick = () => {
    this.context.router.push('/other/route')
  }
}

For reason(s) I'm not aware of, browserHistory.push() did not work for me, albeit browserHistory.goBack() and .goForward() did.

flashmatrix
  • 185
  • 1
  • 2
  • 6