0

Hello I' m building a project where I have used reactjs redux and react-router-dom.

For some strange reason that I can't figure out, when I navigate to http://localhost:3000 I get the following error: Warning: You tried to redirect to the same route you're currently on: "/signin"

I have tried many things from similar questions but with no luck. Perhaps Switch does not work? or I need a fresh pair of eyes because this might be obvious...

Bellow is my source code:

router.jsx

import React from 'react';
import { connect } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
import Routes from '../routes/index.jsx';

// Global Components
import CustomNavbar from '../navbar/index.jsx';
import Sidemenu from '../sidemenu/index.jsx';
import Emulator from '../emulator/index.jsx';

// Styles
import 'font-awesome/css/font-awesome.css';
import 'bootstrap/dist/css/bootstrap.css';
import '../../sass/style.scss';
import '../../sass/router.scss';

class CustomRouter extends React.Component {

    constructor(props) {
        super(props);
    }

    isSignedin = () => {
        return this.props.user.authenticated;
    }

    isSidemenuOpen = () => {
        return this.props.app.sidemenu.open;
    }

    isEmulatorOpen = () => {
        return this.props.app.emulator.open;
    }

    getWrapperClassName = () => {

        let classList = [];

        if (this.isSignedin()) {
            classList.push('authenticated');
        }

        if (this.isSidemenuOpen()) {
            classList.push('sidemenu');
        }

        if (this.isEmulatorOpen()) {
            classList.push('emulator');
        }

        return classList.join(' ');

    }

    render = () => {

        return (
            <BrowserRouter>

                <div id='wrapper' className={this.getWrapperClassName()}>

                    {(() => {

                        if (this.isSignedin()) {

                            return (
                                <React.Fragment>
                                    <Sidemenu />
                                    <CustomNavbar />
                                    <Emulator />
                                </React.Fragment>
                            )

                        } else {
                            return null;
                        }

                    })()}

                    <div id='page-content'>
                        <div className='p-4'>
                            <Routes />
                        </div>
                    </div>

                </div>

            </BrowserRouter>
        )

    }

}

const mapStateToProps = (state) => {
    return {
        app: state.appReducer,
        user: state.userReducer
    }
}

export default connect(mapStateToProps, null, null, { withRef: true })(CustomRouter);

routes.jsx

import React from 'react';
import { withRouter, Switch, Route } from 'react-router-dom';
import { TransitionGroup, CSSTransition } from 'react-transition-group';
import AuthenticatedRoute from '../authenticatedRoute/index.jsx';
import UnauthenticatedRoute from '../unauthenticatedRoute/index.jsx';

// Root Components
import Home from '../routes/home/index.jsx';
import Dashboard from '../routes/dashboard/index.jsx';
import Bots from '../routes/bots/index.jsx';
import Actions from '../routes/actions/index.jsx';
import Flows from '../routes/flows/index.jsx';
import Sessions from '../routes/sessions/index.jsx';
import Users from '../routes/users/index.jsx';
import Signin from '../routes/signin/index.jsx';
import Signup from '../routes/signup/index.jsx';
import Reset from '../routes/reset/index.jsx';
import NotFound from '../routes/notfound/index.jsx';

const Routes = ({ location }) => {
    return (
        <TransitionGroup className='transition-group'>
            <CSSTransition key={location.key} classNames='fade' timeout={{ enter: 300, exit: 300 }}>
                <section className='route-group'>
                    <Switch location={location} >
                        <Route path='/' component={Home} exact={true}></Route>
                        <UnauthenticatedRoute path='/signin' component={Signin} exact={true}></UnauthenticatedRoute>
                        <UnauthenticatedRoute path='/signup' component={Signup} exact={true}></UnauthenticatedRoute>
                        <UnauthenticatedRoute path='/reset' component={Reset} exact={true}></UnauthenticatedRoute>
                        {/* <AuthenticatedRoute path='/dashboard' component={Dashboard} exact={true}></AuthenticatedRoute>
                        <AuthenticatedRoute path='/bots/:botId?' component={Bots} exact={true}></AuthenticatedRoute>
                        <AuthenticatedRoute path='/actions/:actionId?' component={Actions} exact={true}></AuthenticatedRoute>
                        <AuthenticatedRoute path='/flows/:flowId?' component={Flows} exact={true}></AuthenticatedRoute>
                        <AuthenticatedRoute path='/users/:userId?' component={Users} exact={true}></AuthenticatedRoute>
                        <AuthenticatedRoute path='/sessions/:sessionId?' component={Sessions} exact={true}></AuthenticatedRoute> */}
                        <Route path='*' component={NotFound}></Route>
                    </Switch>
                </section>
            </CSSTransition>
        </TransitionGroup>
    )
}

export default withRouter(Routes);

home.jsx (/ dummy route component)

it is only used to either redirect you to /dashboard or /signin accordingly

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

import '../../../sass/home.scss';

class Home extends React.Component {

    constructor(props) {

        super(props);

        this.state = this.getInitialState();

    }

    getInitialState = () => {
        return {};
    }

    render = () => {

        let { authenticated } = this.props.user;

        if (authenticated) {
            console.log('Redirecting to "/dashboard" from home');
            return (
                <Redirect to="/dashboard" />
            )

        } else {
            console.log('Redirecting to "/signin" from home');
            return (
                <Redirect to="/signin" />
            )

        }

    }
}

const mapStateToProps = (state) => {
    return {
        app: state.appReducer,
        user: state.userReducer
    }
}

const mapDispatchToProps = (dispatch) => {
    return {}
}

export default connect(mapStateToProps, mapDispatchToProps, null, { withRef: true })(Home);

unauthenticated.jsx

(for routes that are only accessible when not authenticated)

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

class UnauthenticatedRoute extends React.Component {

    constructor(props) {
        super(props);
    }


    isSignedin = () => {
        return this.props.user.authenticated;
    }

    render = () => {

        let { component: Component, ...rest } = this.props;

        console.log('Unauthenticated:', !this.isSignedin() ? `Rendering` : `Redirecting`);

        return (

            <Route {...rest} render={(props) => (
                !this.isSignedin() ? (
                    <Component {...props} />
                ) : (
                        <Redirect to='/dashboard' />
                    )
            )} />

        )

    }

}

const mapStateToProps = (state) => {
    return {
        app: state.appReducer,
        user: state.userReducer
    }
}

const mapDispatchToProps = (dispatch) => {
    return {}
}

export default withRouter(connect(mapStateToProps, mapDispatchToProps, null, { withRef: true })(UnauthenticatedRoute));

authenticated.jsx

(for routes that are only accessible when authenticated)

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

class AuthenticatedRoute extends React.Component {

    constructor(props) {
        super(props);
    }

    isSignedin = () => {
        return this.props.user.authenticated;
    }

    render = () => {

        let { component: Component, ...rest } = this.props;

        console.log('Authenticated:', this.isSignedin() ? `Rendering` : `Redirecting`);

        return (

            <Route {...rest} render={(props) => (
                this.isSignedin() ? (
                    <Component {...props} />
                ) : (
                        <Redirect to='/signin' />
                    )
            )} />

        )

    }

}

const mapStateToProps = (state) => {
    return {
        app: state.appReducer,
        user: state.userReducer
    }
}

const mapDispatchToProps = (dispatch) => {
    return {}
}

export default withRouter(connect(mapStateToProps, mapDispatchToProps, null, { withRef: true })(AuthenticatedRoute));
syd619
  • 2,952
  • 4
  • 39
  • 75
  • PS: The problem occurs only when I navigate to '/'. If i navigate to /signin there are no console errors. – syd619 Oct 06 '18 at 15:05
  • Are you sure that you have not misnamed the components of Authenticated and Unauthenticated. Check please source code and name. If ok please tell the logs(console.logs), that that appear. – Talgat Saribayev Oct 06 '18 at 15:25
  • The only place those 2 are used are inside routes.jsx. Names seem ok since the only unauthenticated routes are signin signup and reset. The '/' route is a simple route component. The logs print the error mentioned in description 5 times – syd619 Oct 06 '18 at 15:41
  • Haven't worked through the logic on this, but what if `isSignedIn()` called inside nested children `render()`s is always failing because it's not bound to correct `this` context? Does adding `this.isSignedIn = this.isSignedIn.bind(this)` to appropriate constructors help? – Andy Taton Oct 06 '18 at 16:32
  • it's not failing because it is declared as an arrow function, and latest babel transpile's it preserving th context, hence you dont need to bind anymore @AndyTaton – syd619 Oct 06 '18 at 16:43

2 Answers2

0

Try simplifying and separating your protected routes into a single RequireAuth component. With the help of Redux, we can then expose the routes when the user has been authenticated.

You may be able to get away with using React's Context provider, but either way, you'll need to utilize some sort of HOC that can pass down its state/methods to children. Otherwise, it's going to be a nested nightmare of HOC state and methods being passed back and forth.

Working example: https://codesandbox.io/s/88qmx06082 (react-router v3 -- one the reasons why I use and prefer v3 because its super easy to nest routes)

Working example: https://codesandbox.io/s/yqo75n896x (react-router v4)

routes/index.js (v4)

import React from "react";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import { createStore } from "redux";
import { Provider } from "react-redux";
import Home from "../components/Home";
import Header from "../containers/Header";
import Info from "../components/Info";
import Sponsors from "../components/Sponsors";
import Signin from "../containers/Signin";
import RequireAuth from "../containers/RequireAuth";
import rootReducer from "../reducers";

const store = createStore(rootReducer);

export default () => (
  <Provider store={store}>
    <BrowserRouter>
      <div>
        <Header />
        <Switch>
          <Route exact path="/" component={Home} />
          <Route path="/info" component={Info} />
          <Route path="/sponsors" component={Sponsors} />
          <Route path="/protected" component={RequireAuth} />
          <Route path="/signin" component={Signin} />
        </Switch>
      </div>
    </BrowserRouter>
  </Provider>
);

containers/RequireAuth.js (v4)

import React from "react";
import { Route, Redirect } from "react-router-dom";
import { connect } from "react-redux";
import ShowPlayerRoster from "../components/ShowPlayerRoster";
import ShowPlayerStats from "../components/ShowPlayerStats";
import Schedule from "../components/Schedule";

const RequireAuth = ({ match: { path }, isAuthenticated }) =>
  !isAuthenticated ? (
    <Redirect to="/signin" />
  ) : (
    <div>
      <Route exact path={`${path}/roster`} component={ShowPlayerRoster} />
      <Route path={`${path}/roster/:id`} component={ShowPlayerStats} />
      <Route path={`${path}/schedule`} component={Schedule} />
    </div>
  );

export default connect(state => ({
  isAuthenticated: state.auth.isAuthenticated
}))(RequireAuth);
Matt Carlotta
  • 13,627
  • 3
  • 24
  • 39
0

The solution was the following and it was a matter of logic as far as I can tell.There were no multiple components mounting if you might wonder. It had to do with AuthenticatedRoute & UnauthenticatedRoute component. Also I completely removed home component.

Authenticate render method

render = () => {

    let { component: Component, ...rest } = this.props;

    return (

        <Route {...rest} render={(props) => (
            this.isSignedin() ? (
                <Component {...props} />
            ) : (
                    props.location.pathname === '/signin' ? (null) : (<Redirect to='/signin' />)
                )
        )} />

    )

}

Unauthenticated render method

render = () => {

    let { component: Component, ...rest } = this.props;

    return (

        <Route {...rest} render={(props) => (
            !this.isSignedin() ? (
                <Component {...props} />
            ) : (
                    props.location.pathname === '/' ? (null) : (<Redirect to='/' />)
                )
        )} />

    )

}
syd619
  • 2,952
  • 4
  • 39
  • 75