Moon
Moon

Reputation: 35265

How to properly redirect with React Router?

I am trying to setup a simple authentication redirection with React and React Router.

Here are important packages and their versions I am using:

{
  "react": "^16.3.1",
  "react-dev-utils": "^5.0.1",
  "react-dom": "^16.3.1",
  "react-redux": "^5.0.7",
  "react-router-dom": "^4.3.0-rc.2",
  "redux": "^3.7.2",
}

This is what I was to achieve:

1. If user is not signed in and     going to /signin -- allow
2. If user is     signed in and     going to /signin -- redirect to /
3. If user is not signed in and not going to /signin -- redirect to /signin
4. If user is     signed in and not going to /signin -- allow

With the following code, redirection seems to be happening -- I see correct url in browser.

However, for the case user is signed in and is going to /signin I do see browser's URL changes to / but the Dashboard component is not rendered.

Here is relevant code:

app.js

import React, { Component } from "react";
import { Fabric } from "office-ui-fabric-react/lib/Fabric";
import { BrowserRouter as Router } from "react-router-dom";
import SmartRoute from "./smart-route";
import Header from "./ui/header";
import Dashboard from "./dashboard";
import SignIn from "./auth/signin";
import styles from "./app.scss";

class App extends Component {
  render() {
    return (
      <Router>
        <Fabric>
          <Header />
          <section className={styles.main}>
            <SmartRoute exact={true} path="/" component={Dashboard} />
            <SmartRoute exact={true} path="/signin" component={SignIn} />
          </section>
        </Fabric>
      </Router>
    );
  }
}

export default App;

smart-route.js

import React from "react";
import { Route, Redirect } from "react-router-dom";
import { connect } from "react-redux";

const renderComponent = (props, isAuthenticated, Component) => {
  const path = props.match.path;
  if (path === "/signin" && !isAuthenticated) return <Component {...props} />;
  if (path === "/signin" && isAuthenticated) return <Redirect to="/" />;
  return isAuthenticated ? <Component {...props} /> : <Redirect to="/signin" />;
};

const SmartRoute = ({ component: Component, isAuthenticated, ...rest }) => (
  <Route
    {...rest}
    render={props => renderComponent(props, isAuthenticated, Component)}
  />
);

const mapStateToProps = state => ({
  isAuthenticated: state.session.authUser !== null
});

export default connect(mapStateToProps)(SmartRoute);

dashboard.js

import React from "react";
const Dashboard = () => <section>Dashboard</section>;
export default Dashboard;

Upvotes: 0

Views: 1028

Answers (1)

Moti Korets
Moti Korets

Reputation: 3748

The problem is update blocking. To solve it you should use with withRouter HOC like this

import { withRouter } from 'react-router'
//.. 
export default withRouter(connect(mapStateToProps)(SmartRoute))

The reason it is happening is redux implements shouldComponentUpadte method which is unaware of location changes (and thus will not rerender the SmartRoute component). To overcome this you can pass location as a prop to SmartRoute (more efficient but not always straightforward) component or wrap it with withRouter (quick and dirty but might have performance issues). Read the doc for more in depth discussion.

Upvotes: 2

Related Questions