You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

已登录用户禁止访问登录页面问题:登录页闪显后跳转

解决已登录用户访问登录页闪显问题

问题核心是初始认证状态设为false,组件先渲染登录界面,异步请求完成后才更新状态跳转,导致页面短暂闪显。以下是具体解决方案:

1. 优化初始认证状态判断

利用登录成功后存入的localStorage直接初始化认证状态,减少等待异步请求的时间:

const [isAuthenticated, setIsAuthenticated] = useState(() => {
  // 从localStorage读取初始认证状态
  return localStorage.getItem("authenticated") === "true";
});

2. 添加加载状态,避免提前渲染登录界面

新增isLoading状态,在异步请求完成前不渲染登录界面:

const [isLoading, setIsLoading] = useState(true); // 初始为加载中

在useEffect的请求回调里,无论认证结果如何,都标记加载完成:

useEffect(() => {
  fetch("http://localhost:8000/session/", { credentials: "include" })
    .then((res) => res.json())
    .then((data) => {
      if (data.isAuthenticated) {
        setIsAuthenticated(true);
        localStorage.setItem("authenticated", true); // 同步localStorage状态
      } else {
        setIsAuthenticated(false);
        localStorage.removeItem("authenticated"); // 同步localStorage状态
        getCsrf();
      }
      setIsLoading(false); // 请求完成,结束加载
    })
    .catch((err) => {
      console.log(err);
      setIsLoading(false); // 出错也结束加载
    });
}, []);

3. 调整组件返回逻辑

加载状态时返回占位容器(保持布局一致,避免页面跳动),认证通过则跳转,否则渲染登录界面:

// 加载中返回占位
if (isLoading) {
  return <div className="login-box m-auto"></div>;
}

if(isAuthenticated) {   
  return <Navigate replace to="/admin/dashboard" />;
} else {
  return loginScreen;
}

完整修改后的组件代码

import { useEffect, useState } from "react"
import { Navigate } from "react-router-dom";
import { useNavigate } from "react-router-dom";
 
function AdminLogin() {
    const navigate = useNavigate()
    // 从localStorage初始化认证状态
    const [isAuthenticated, setIsAuthenticated] = useState(() => {
        return localStorage.getItem("authenticated") === "true";
    });
    const [isLoading, setIsLoading] = useState(true); // 新增加载状态
    const [csrf, setCsrf] = useState('')
    const [username, setUsername] = useState('')
    const [password, setPassword] = useState('')
    
    function getCsrf(){
        fetch("http://localhost:8000/csrf/", { credentials: "include" })
              .then((res) => {
                let csrfToken = res.headers.get("X-CSRFToken");
                setCsrf({csrf: csrfToken});
              })
              .catch((err) => {
                console.log(err);
              })
    }

    const login = (event) => {
        event.preventDefault();
        fetch("http://localhost:8000/login/", {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
            "X-CSRFToken": csrf.csrf,
          },
          credentials: "include",
          body: JSON.stringify({username: username, password: password}),
        })
        .then(isResponseOk)
        .then((data) => {
          console.log(data);
          setIsAuthenticated(true)
          localStorage.setItem("authenticated", true);
          setUsername('')
          setPassword('')
        })
        .catch((err) => {
            console.log('inside login catch')
            console.log(csrf.csrf, 'catch')
          console.log(err);
        });
      }

      const isResponseOk = (response) =>{
        if (response.status >= 200 && response.status <= 299) {
          return response.json();
        } else {
          console.log(response)
          throw Error(response.statusText);
        }
      }

      useEffect(() => {
        fetch("http://localhost:8000/session/", { credentials: "include" })
          .then((res) => res.json())
          .then((data) => {
            if (data.isAuthenticated) {
              setIsAuthenticated(true);
              localStorage.setItem("authenticated", true);
            } else {
              setIsAuthenticated(false);
              localStorage.removeItem("authenticated");
              getCsrf();
            }
            setIsLoading(false); // 结束加载
          })
          .catch((err) => {
            console.log(err);
            setIsLoading(false); // 出错也结束加载
          });
      }, [])

    const handleUsername = (e) => {
        setUsername(e.target.value)
    }

    const handlePassword = (e) => {
        setPassword(e.target.value)
    }

    const loginScreen = (  
    <div className="login-box m-auto">
    <div className="card">
        <div className="card-body login-card-body">
        <p className="login-box-msg">Sign in to start your session</p>
        <form method="post" onSubmit={login}>
            <div className="input-group mb-3">
            <input required type="text" value={username} onChange={handleUsername} className="form-control" placeholder="Username" />
            <div className="input-group-append">
                <div className="input-group-text">
                <span className="fas fa-envelope" />
                </div>
            </div>
            </div>
            <div className="input-group mb-3">
            <input required type="password" value={password} onChange={handlePassword} className="form-control" placeholder="Password" />
            <div className="input-group-append">
                <div className="input-group-text">
                <span className="fas fa-lock" />
                </div>
            </div>
            </div>
            <div className="row">
            <div className="col-4">
                <button type="submit" className="btn btn-primary btn-block">Sign In</button>
            </div>
            </div>
        </form>
    </div>
  </div>
  </div>)

    // 加载中返回占位
    if (isLoading) {
        return <div className="login-box m-auto"></div>;
    }

    if(isAuthenticated) {   
        return <Navigate replace to="/admin/dashboard" />;
    } else {
        return loginScreen;
    }
}

内容的提问来源于stack exchange,提问作者sash

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 02:40:37