Reputation: 921
I've read a lot of docs and SOF questions about this subject I used a lot of methods to do this but none of them worked in a way I need. I suppose I am missing something important. It's hard to provide detailed and relevant code on every navigation method I used, I'll try it.
Here some introdactory imformation. I had react-redux app, I need to get state from server and redirect to page according to this state.
Simplified version of my App class render method:
render() {
console.log("App.render");
console.log(this.state);
return (
<div id='game-container' width="1126" height="634">
<Router>
<div>
<Route exact path="/" component={Page1}/>
<Route path="/bets" component={Page2}/>
<Route path="/games" component={Page3}/>
<Route path="/newpage" component={Page4}/>
<Route path="/info" component={ Info }/>
</div>
</Router>
<Overlay/>
</div>
);
}
}
App component wrapped with BrowserRouter in index.js file
ReactDOM.render(
<Provider store={myStore}>
<BrowserRouter>
<App assets={ assets } locale={ qs['locale']} token={ qs['token']} sounds={ sounds } />
</BrowserRouter>
</Provider>
, document.querySelector('.game-wrapper'));
Method 1. withRouter
For it's realisation I added this code:
App.propTypes = {
history: React.PropTypes.shape({
push: React.PropTypes.func.isRequired,
}).isRequired,
location: React.PropTypes.object.isRequired,
};
function mapStateToProps(state) {
console.log("app#mapStateToProps");
console.log(state.session);
return { session: state.session }
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({ fetchInitialSession }, dispatch);
}
export default withRouter(connect(mapStateToProps, { fetchInitialSession })(App));
and then when I am getting session info I try:
this.props.history.push("/newpage");
url in browser is changing but visual page does not.
Method 2. with context router
For this just added
App.contextTypes = {
router: React.PropTypes.object
};
and then when I am getting session info I try:
this.context.router.history.push("/newpage");
Same result as with first method (url is changed but page doesn't).
Method 3. Redirect (found here Programmatically navigate using react router V4)
So my render method looks like this:
render() {
console.log("App.render");
console.log(this.state);
return (
<div id='game-container' width="1126" height="634">
<Router>
<div>
<Redirect to={{ pathname: this.state.redirectTo }}/>
<Route exact path="/" component={Page1}/>
<Route path="/bets" component={Page2}/>
<Route path="/games" component={Page3}/>
<Route path="/newpage" component={Page4}/>
<Route path="/info" component={ Info }/>
</div>
</Router>
<Overlay/>
</div>
);
}
}
And when I want to change page I set new state:
this.setState({ redirectTo: "/newpage" });
This method gives strange results. When I set initial state in constructor it redirects to any page. But when I use this method anywhere in code I see in debug that render method called with my new state but redirect never happens!
Upvotes: 3
Views: 1430
Reputation: 934
In v4 method 3 should be the correct method (using the Redirect
component). The problem you're having seems to be because Redirect
only takes effect the first time it's mounted but you put Redirect at the top level in your router and it only gets mounted once.
So if you render <Redirect to={{ pathname: this.state.redirectTo }}/>
it will only redirect the very first time it's rendered, but it won't redirect if you update the state using:
this.setState({ redirectTo: "/newpage" });
This is why it only worked for you when you set the redirectTo
in the constructor but didnt redirect again after that.
Instead you're probably going to have to use Redirect
in every component/route you want to trigger a redirect, it's not as tedious as it sounds though because you can just create a custom route component to use instead of the typical Route
component and handle all the redirect logic once in your custom component.
You can find an example here where a PrivateRoute
and PublicRoute
are used instead of the normal Route
to check if the user is logged in and redirect accordingly:
https://github.com/tylermcginnis/react-router-firebase-auth/blob/master/src/components/index.js
Here's how the PrivateRoute which should only be accessible to logged in users looks like:
function PrivateRoute ({component: Component, authed, ...rest}) {
return (
<Route
{...rest}
render={(props) => authed === true
? <Component {...props} />
: <Redirect to={{pathname: '/login', state: {from: props.location}}} />}
/>
)
}
Upvotes: 2