React 路由器会短暂显示登录屏幕,然后如果用户已登录,则会重定向到仪表板

Posted

技术标签:

【中文标题】React 路由器会短暂显示登录屏幕,然后如果用户已登录,则会重定向到仪表板【英文标题】:React router briefly shows the login screen and then redirects to dashboard if user is logged in 【发布时间】:2021-03-19 02:13:05 【问题描述】:

我有一个登录页面、一个仪表板页面以及其他一些页面。我使用firebase作为后端。 我在登录组件上执行了条件渲染,以检查用户是否登录。 (如果已登录,则会重定向到仪表板)。 如果用户未登录,我已将 ProtectedRoutes 用于仪表板和其他人以重定向到登录页面。

发生的情况是,当我在登录时重新加载仪表板页面或任何其他页面时,我简要地看到了 Login 组件,然后它执行 firebase "auth.onAuthStateChanged" 方法并设置登录状态为 true 并将我重定向回仪表板。我明白为什么我会短暂看到登录页面,但我不知道如何解决这个问题。

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"  />
                        <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)

【问题讨论】:

当你重新加载页面时,浏览器会向服务器发送一个 fetch 请求,并检索一个全新的副本,然后再次被浏览器解析 【参考方案1】:

我不会编辑你的代码,只解释基本原理: 在渲染受保护的路线期间,您有 2 个选项:

要么等待身份验证解决(props.auth.loggedin 为真或假,但不是未定义 - 假设您从 BE 收到这些值)。同时,显示加载屏幕。解决此问题后,您将显示您的页面,或重定向到登录。

第二种选择是继续并开始渲染私有(受保护)路由。用户将看不到任何私人数据,因为这些数据来自后端,并且这些后端调用也受到后端身份验证的保护。这种方法将允许您直接开始渲染受保护路由的内容,同时等待 props.auth.loggedin 从后端到达 - 在您的 App 组件中,设置 React.useEffect 监听 props.auth.loggedin .如果用户未通过身份验证,则将他重定向到登录页面。如果是,则不会有任何变化(或者您也可以让您的状态管理人员知道这件事)。

为了您的灵感,我在代码中包含了我的做法:

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>
    )
  />
);
);

然而,在这段代码中,我在 Mobex 中存储了有关用户是否通过身份验证的信息,并监听 Mobex 状态变化 - 因此当 Mobex 意识到用户未通过身份验证时,它将触发该重定向语句的执行。

【讨论】:

感谢您的回复。您提到的第一个选项是我过去尝试过的选项,并且效果很好。但是,我很好奇是否可以在不显示加载屏幕的情况下解决我的问题。 (例如,是否可以在任何组件呈现之前调用后端服务,然后根据我返回的结果,我可以呈现登录页面或任何其他)第二个选项是我无法在我的网络应用程序中实现的因为我绝对不希望用户在未登录的情况下看到任何页面,所以该页面是否显示敏感数据并不重要 是的,你不必显示加载屏幕,你可以什么都不显示。 (这是不好的做法)。但是在您获得身份验证响应之前,render 需要返回一些东西。在慢速连接中(您可以在 Chrome 的检查器工具中的网络选项卡中模拟慢速连接)身份验证请求可能需要几秒钟才能解决。因此,如果您选择第一个选项,那么您必须等待此解决方案,并且没有其他方法可以解决,这是合乎逻辑的。由于那些网络速度慢的用户,我建议显示加载屏幕而不是空白屏幕。

以上是关于React 路由器会短暂显示登录屏幕,然后如果用户已登录,则会重定向到仪表板的主要内容,如果未能解决你的问题,请参考以下文章

Facebook 登录 Segue 加载,然后返回登录屏幕

阻止用户在 React Native 中成功登录后返回登录屏幕

登录屏幕消失并在登录失败时重新出现

在 iPhone 上晃动视觉效果(不晃动设备)

火狐登录路由器,未输入密码就提示用户名密码错误

首次登录时的 React-Native 欢迎屏幕