3

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!

Cœur
  • 32,421
  • 21
  • 173
  • 232
Dmitry Malugin
  • 798
  • 1
  • 6
  • 22
  • When you are trying to set the state, component is already mounted. Why not to use `history.push` instead of changing the state? – Roman Maksimov Apr 21 '17 at 12:50
  • Did you read about my 1 and 2 methods? – Dmitry Malugin Apr 21 '17 at 13:00
  • Let's start from the page path. You are trying to open something by `/newpage` path, but you haven't specified it anywhere. Where are you handling it? – Roman Maksimov Apr 21 '17 at 13:28
  • Sorry I did some adjustments when posted code here, /newpage specified, edited my question – Dmitry Malugin Apr 21 '17 at 14:21
  • The code you posted doesn't look like it will work at all. Where is `` in your `App` component coming from and isn't that a redundancy since you already have `` in your `index.js`? I would suggest making a simpler example with complete code including your `require` statements so we can reproduce on our end to debug it. – Todd Chaffee Apr 21 '17 at 15:39
  • When I remove Router tags from App render, everything works (except redirect), so it's redundant I agree. But when I remove BrowserRouter from index.js, I am getting error Uncaught TypeError: Cannot read property 'route' of undefined – Dmitry Malugin Apr 21 '17 at 17:16

1 Answers1

2

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}}} />}
    />
  )
}
Ahmed Wagdi
  • 934
  • 5
  • 9