3

I am using a similar code like this to redirect in my app after users logged in. The code looks like the following:

import React, { Component } from 'react'
import { Redirect } from 'react-router'

export default class LoginForm extends Component {
  constructor () {
    super();
    this.state = {
      fireRedirect: false
    }
  }

  submitForm = (e) => {
    e.preventDefault()
    //if login success
    this.setState({ fireRedirect: true })
  }

  render () {
    const { from } = this.props.location.state || '/'
    const { fireRedirect } = this.state

    return (
      <div>
        <form onSubmit={this.submitForm}>
          <button type="submit">Submit</button>
        </form>
        {fireRedirect && (
          <Redirect to={from || '/home'}/>
        )}
      </div>
    )

  }
}

Works fine when a successful login has been triggered. But there is the case, that logged in users enter the login page and should be automatically redirected to the "home" page (or whatever other page).

How can I use the Redirect component without rendering the current component and without (as far as I understand discouraged) imperative pushing to the history (e.g. in componentWillMount)?

Jankapunkt
  • 7,161
  • 4
  • 24
  • 49
  • 1
    You're using wrong approach. Redirect should not be used alone inside component. Conversely, you have to place your component into `Route` along with Redirect. See example in [the official docs](https://reacttraining.com/react-router/web/api/Redirect). – hindmost Jun 06 '17 at 13:52
  • Thank you, yes my approach was somehow wrong. I now have all my routing and redirecting in my Router defined. – Jankapunkt Jun 06 '17 at 14:26

2 Answers2

13

Solution 1

You could use withRouter HOC to access history via props.

Import withRouter.

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

Then wrap with HOC.

// Example code
export default withRouter(connect(...))(Component)

Now you can access this.props.history. For example use it with componentDidMount().

componentDidMount() {
  const { history } = this.props;

  if (this.props.authenticated) {
    history.push('/private-route');
  }
}

Solution 2 Much better

Here is example on reacttraining.

Which would perfectly work for you.

But you just need to create LoginRoute to handle problem you described.

const LoginRoute = ({ component: Component, ...rest }) => (
  <Route
    {...rest} render={props => (
    fakeAuth.isAuthenticated ? (
        <Redirect to={{
          pathname: '/private-route',
          state: { from: props.location }
        }} />
      ) : (
        <Component {...props} />
      )
  )} />
);

and inside <Router /> just replace

<Route path="/login" component={Login}/>

with

<LoginRoute path="/login" component={Login}/>

Now everytime somebody will try to access /login route as authenticated user, he will be redirected to /private-route. It's even better solution because it doesn't mount your LoginComponent if condition isn't met.

loelsonk
  • 3,186
  • 2
  • 13
  • 22
  • Solution 2 seems to be what I am looking for! Checking it out, now. – Jankapunkt Jun 06 '17 at 14:08
  • it definitely works, I am using this approach in my project. Let me know if you need help with it. – loelsonk Jun 06 '17 at 14:13
  • Solution 2 works perfect AND I could ged rid of routing logic within my components. Thanks a lot! – Jankapunkt Jun 06 '17 at 14:23
  • Solution 1 has some synthax issues. The import is react-router: `import { withRouter } from "react-router";` and export default withRouter brackets should cover in this way: `export default withRouter(connect(...)(Component))` – lortschi Feb 03 '21 at 22:25
2

Here is another solution which doesn't touch React stuff at all. E.g. if you need to navigate inside redux-saga.

Have file history.js:

import {createBrowserHistory} from 'history';
export default createBrowserHistory();

Somewhere where you define routes, don't use browser router but just general <Router/>:

import history from 'utils/history';

...

<Router history={history}>
  <Route path="/" component={App}/>
</Router>

That's it. Now you can use same history import and push new route.

In any part of your app:

import history from 'utils/history';
history.push('/foo');

In saga:

import {call} from 'redux-saga/effects';
import history from 'utils/history';

 ... 

history.push('/foo');
yield call(history.push, '/foo');
Lukas Liesis
  • 17,690
  • 7
  • 93
  • 89