Programmatically Navigate using react-router

JavascriptReactjsReduxReact RouterReact Router-V4

Javascript Problem Overview


I am developing an application in which I check if the user is not loggedIn. I have to display the login form, else dispatch an action that would change the route and load other component. Here is my code:

render() {
    if (isLoggedIn) {
        // dispatch an action to change the route
    }
    // return login component
    <Login />
}

How can I achieve this as I cannot change states inside the render function.

Javascript Solutions


Solution 1 - Javascript

Considering you are using react-router v4

Use your component with withRouter and use history.push from props to change the route. You need to make use of withRouter only when your component is not receiving the Router props, this may happen in cases when your component is a nested child of a component rendered by the Router and you haven't passed the Router props to it or when the component is not linked to the Router at all and is rendered as a separate component from the Routes.

import {withRouter} from 'react-router';

class App extends React.Component {
     ...
     componenDidMount() {
        // get isLoggedIn from localStorage or API call
        if (isLoggedIn) {
             // dispatch an action to change the route
             this.props.history.push('/home');
        }
     }
     render() {
         // return login component
         return <Login />
    }
}


export default withRouter(App);

> Important Note > > If you are using withRouter to prevent updates from being blocked by > shouldComponentUpdate, it is important that withRouter wraps the > component that implements shouldComponentUpdate. For example, when > using Redux: > > // This gets around shouldComponentUpdate > > withRouter(connect(...)(MyComponent)) > > // This does not > > connect(...)(withRouter(MyComponent))

or you could use Redirect

import {withRouter} from 'react-router';

class App extends React.Component {
     ...
     render() {
         if(isLoggedIn) {
              return <Redirect to="/home"/>
         }
         // return login component
         return <Login />
    }
}

With react-router v2 or react-router v3, you can make use of context to dynamically change the route like

class App extends React.Component {
     ...
     render() {
         if (isLoggedIn) {
             // dispatch an action to change the route
             this.context.router.push('/home');
         }
         // return login component
         return <Login />
    }
}

App.contextTypes = {
    router: React.PropTypes.object.isRequired
}
export default App;

or use

import { browserHistory } from 'react-router';
browserHistory.push('/some/path');

Solution 2 - Javascript

In react-router version 4:

import React from 'react'
import { BrowserRouter as Router, Route, Redirect} from 'react-router-dom'

const Example = () => (

  if (isLoggedIn) {
    <OtherComponent />

  } else {

    <Router>
      <Redirect push to="/login" />
      <Route path="/login" component={Login}/>
    </Router>

  }
)

const Login = () => (
    <h1>Form Components</h1>
    ...
)

export default Example;

Solution 3 - Javascript

Another alternative is to handle this using Thunk-style asynchronous actions (which are safe/allowed to have side-effects).

If you use Thunk, you can inject the same history object into both your <Router> component and Thunk actions using thunk.withExtraArgument, like this:

import React from 'react'
import { BrowserRouter as Router, Route, Redirect} from 'react-router-dom'
import { createBrowserHistory } from "history"
import { applyMiddleware, createStore } from "redux"
import thunk from "redux-thunk"

const history = createBrowserHistory()

const middlewares = applyMiddleware(thunk.withExtraArgument({history}))
const store = createStore(appReducer, middlewares)

render(
  <Provider store={store}
    <Router history={history}>
      <Route path="*" component={CatchAll} />
    </Router
  </Provider>,
appDiv)

Then in your action-creators, you will have a history instance that is safe to use with ReactRouter, so you can just trigger a regular Redux event if you're not logged in:

// meanwhile... in action-creators.js
export const notLoggedIn = () => {
  return (dispatch, getState, {history}) => {
    history.push(`/login`)
  }
}

Another advantage of this is that the url is easier to handle, now, so we can put redirect info on the query string, etc.

You can try still doing this check in your Render methods, but if it causes problems, you might consider doing it in componentDidMount, or elsewhere in the lifecycle (although also I understand the desire to stick with Stateless Functional Compeonents!)

You can still use Redux and mapDispatchToProps to inject the action creator into your comptonent, so your component is still only loosely connected to Redux.

Solution 4 - Javascript

This is my handle loggedIn. react-router v4

PrivateRoute is allow enter path if user is loggedIn and save the token to localStorge

    function PrivateRoute({ component: Component, ...rest }) {
     return (
        <Route
          {...rest}
          render={props => (localStorage.token) ? <Component {...props} /> : (
            <Redirect
              to={{
                pathname: '/signin',
                state: { from: props.location },
              }}
            />
          )
          }
        />
      );
    }

Define all paths in your app in here

export default (
  <main>
    <Switch>
      <Route exact path="/signin" component={SignIn} />
      <Route exact path="/signup" component={SignUp} />
      <PrivateRoute path="/" component={Home} />
    </Switch>
  </main>
);

Solution 5 - Javascript

Those who are facing issues in implementing this on react-router v4. Here is a working solution for navigating through the react app programmatically.

history.js

import createHistory from 'history/createBrowserHistory'

export default createHistory()

App.js OR Route.jsx. Pass history as a prop to your Router.

import { Router, Route } from 'react-router-dom'
import history from './history'
...
<Router history={history}>
 <Route path="/test" component={Test}/>
</Router>

You can use push() to navigate.

import history from './history' 

...

render() {
     if (isLoggedIn) {
         history.push('/test') // this should change the url and re-render Test component
     }
     // return login component
     <Login />
}

All thanks to this comment: https://github.com/ReactTraining/react-router/issues/3498#issuecomment-301057248

Solution 6 - Javascript

render(){ 

    return (
        <div>  
            { this.props.redirect ? <Redirect to="/" /> :'' } 
            <div>
                add here component codes
            </div>
        </div>
    );
} 

Solution 7 - Javascript

I would suggest you to use connected-react-router https://github.com/supasate/connected-react-router which helps to perform navigation even from reducers/actions if you want. it is well documented and easy to configure

Solution 8 - Javascript

I was able to use history within stateless functional component, using withRouter following way (needed to ignore typescript warning):

import { withRouter } from 'react-router-dom';

...

type Props = { myProp: boolean };

// @ts-ignore
export const MyComponent: FC<Props> = withRouter(({ myProp, history }) => {

...

})

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionGaurav MehtaView Question on Stackoverflow
Solution 1 - JavascriptShubham KhatriView Answer on Stackoverflow
Solution 2 - JavascripttgrrrView Answer on Stackoverflow
Solution 3 - JavascriptChris JaynesView Answer on Stackoverflow
Solution 4 - JavascriptrianView Answer on Stackoverflow
Solution 5 - JavascriptreflexgravityView Answer on Stackoverflow
Solution 6 - JavascriptAraz BabayevView Answer on Stackoverflow
Solution 7 - JavascriptDenis RudovView Answer on Stackoverflow
Solution 8 - JavascriptegoView Answer on Stackoverflow