React router briefly shows the login screen and then redirects to dashboard if user is logged in

Viewed 1374

I have a Login page, a dashboard page, and a few others as well. I am using firebase as the backend. I have performed a conditional render on the login component to check if the user is logged in or not. (if logged in, then it redirects to dashboard). I have used ProtectedRoutes for the dashboard and others to redirect to the Login page if the user is not logged in.

what happens is that when I reload the dashboard page or any other page while logged in is true, I briefly see the Login component then it performs the firebase "auth.onAuthStateChanged" method and sets the logged-in state to true and redirects me back to dashboard. I do understand why I see the login page briefly what I don't know is how to fix that.

import React, { useState } from 'react'
import { connect } from "react-redux"
import { signin, autoSignin } from "../Redux/Actions/AuthActions"
import { Redirect } from 'react-router-dom'

const Login = (props) => {

    const [form, setForm] = useState({})
    const [disable, setDisable] = useState(false)



    const handleChange = (e) => {
        setForm({
            ...form,
            [e.target.name]: e.target.value
        })
    }

    const handleSubmit = (e) => {
        setDisable(true)
        e.preventDefault()
        props.signin(form.email, form.password)
        setDisable(false)
    }


    return (
        props.auth.loggedin ?
            <Redirect to="/dashboard" /> :
            <div className="container-fluid login_screen">
                <div className="row justify-content-center">
                    <div className="col-md-4 col-xs-12">
                        <img className="img-fluid login_screen_logo mt-4" src="/images/logo.png" alt="logo" />
                        <h1 className="mt-4 fs-3 heading">LOGIN</h1>
                        <p className="fs-6 mb-3">to access dashboard</p>

                        <form onSubmit={handleSubmit}>
                            <div className="form-floating mb-4 mt-4">
                                <input name="email" onChange={handleChange} required type="email" className="form-control shadow-sm" id="floatingInput" placeholder="room@adil.tower" />
                                <label htmlFor="floatingInput">Email id</label>
                            </div>
                            <div className="form-floating mt-4">
                                <input name="password" onChange={handleChange} required type="password" className="form-control shadow-sm" id="floatingPassword" placeholder="Password" />
                                <label htmlFor="floatingPassword">Password</label>
                            </div>
                            <div className="d-grid gap-2">
                                <button type="submit" className={`btn btn-custom mt-4 btn-lg shadow ${disable ? "disabled" : ""}`}>LOGIN</button>
                            </div>
                        </form>


                    </div>
                </div>

            </div>
    )

}

const mapStateToProps = (state) => {
    return {
        auth: state.auth
    }
}

const mapDispatchToProps = (dispatch) => {
    return {
        signin: (email, pass) => dispatch(signin(email, pass)),
        autoSignin: (uid) => dispatch(autoSignin(uid))
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(Login)

import React from 'react'


const Dashboard = (props) => {

    return (
        <h1>
        dashboard
        </h1>
    )
}


export default Dashboard

import React from 'react'
import { Redirect, Route } from "react-router-dom"

const ProtectedRoute = ({ loggedin, component: Component, ...rest }) => {
    return (
        <Route
            {...rest}
            render={
                (props) => {
                    if (loggedin) {
                        return <Component {...props} />
                    } else {
                        return <Redirect to="/" />
                    }
                }
            }
        />
    )
}

export default ProtectedRoute

import React, { useEffect } from 'react'
import { BrowserRouter as Router, Switch, Route } from "react-router-dom"
import ProtectedRoute from './ProtectedRoute'
import { auth } from "./FirebaseConfig"

// SCREENS
import Dashboard from './Dashboard/Dashboard'
import Login from "./Login/Login"
import Profile from './Profile/Profile'
import Emergency from './Emergency/Emergency'
import Notice from './Notice/Notice'
import Complains from "./Complains/Complains"
import Meetings from "./Meetings/Meetings"

// REDUX STORE & ACTIONS
import { connect } from "react-redux"
import { autoSignin } from "./Redux/Actions/AuthActions"


const App = (props) => {

    useEffect(
        () => {
            auth.onAuthStateChanged(user => {
                if (user) {
                    props.autoSignin(user.uid)
                }

            })
        }, []
    )

    return (
        <Router>
            <Switch>
                <ProtectedRoute loggedin={props.auth.loggedin} exact path="/dashboard" component={Dashboard}></ProtectedRoute>
                <Route exact path="/"><Login /></Route>
                <ProtectedRoute loggedin={props.auth.loggedin} exact path="/profile" component={Profile}></ProtectedRoute>
                <ProtectedRoute loggedin={props.auth.loggedin} exact path="/emergency" component={Emergency}></ProtectedRoute>
                <ProtectedRoute loggedin={props.auth.loggedin} exact path="/notices" component={Notice}></ProtectedRoute>
                <ProtectedRoute loggedin={props.auth.loggedin} exact path="/complains" component={Complains}><Complains /> </ProtectedRoute>
                <ProtectedRoute loggedin={props.auth.loggedin} exact path="/meetings" component={Meetings} ></ProtectedRoute>
            </Switch>
        </Router>
    )
}

const mapStateToProps = (state) => {
    return {
        auth: state.auth
    }
}

const mapDispatchToProps = (dispatch) => {
    return {
        autoSignin: (uid) => dispatch(autoSignin(uid))
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(App)

1 Answers

I won't be editing your code, just explain the basic principle: During rendering of your Protected route, you have 2 options:

Either wait until the authentication is resolved (props.auth.loggedin is true or false, but not undefined - assuming these values you receive from BE). Meanwhile, show a loading screen. When this is resolved, you show your page, or you redirect to Log in.

Second option is to just go ahead and start rendering the private (protected) route. User will not see any private data, as those are coming from backend, and you have those backend calls protected by authentication on backend as well. This approach will allow you to directly start to render the content of protected route, and while doing so, wait for props.auth.loggedin to arrive from backend - in your App component, set up React.useEffect listening to props.auth.loggedin. If user would not be authenticated, you redirect him to login page. If he is, nothing changes (or you might let your state management know about it as well).

I include how I do it in my code for Your inspiration:

export const AppRoute: React.FC<RouteProps> = observer((props: RouteProps) => {
const { user } = useMst();
const { component, ...rest } = props;
if (!user.isAuthenticated) {
  return <Redirect to={"/login"} />;
}

const Component = component as any;

return (
  <Route
    {...rest}
    render={(props) => (
      <Layout>
        <Component {...props} />
      </Layout>
    )}
  />
);
});

In this code, however, I store information about whether user is authenticated in the Mobex, and listen to Mobex state change - so when Mobex will realize user is not authenticated, it will trigger the execution of that redirect statement.

Related