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

React Router页面刷新时认证状态丢失问题排查与解决建议

问题:React Router页面刷新后认证状态丢失,自动跳转到登录页

我正在配置React Router,希望除登录页外的所有路由都显示Navbar。为此创建了Layout组件,未认证时会跳转至登录页。目前把认证状态存在localStorage里,值为true时用户应保持认证并留在当前页面。正常使用时功能没问题,但页面刷新后,就算localStorage里的值是true,用户还是会被跳转到登录页。请问哪里出错了,或者有什么其他实现方案?


App.js

export const AuthContext = createContext();

function App() {
  const stillAuth = window.localStorage.getItem("auth state");
  console.log("still auth: " + stillAuth);
  const [isAuthenticated, setIsAuthenticated] = useState(stillAuth);
  console.log("isAuth: " + isAuthenticated);

  return (
    <div className="App d-flex flex-column" style={{ minHeight: "100vh" }}>
      <AuthContext.Provider value={{ isAuthenticated, setIsAuthenticated }}>
        <Routes>
          <Route path="login" element={<Login />} />
          <Route path="/" element={<Layout />}>
            <Route path="/" element={<Home />} />
            <Route path="/DevExamples" element={<DeveloperExamples />} />
            <Route path="/CliExamples" element={<ClientExamples />} />
            <Route path="*" element={<Error />} />
          </Route>
        </Routes>
      </AuthContext.Provider>

      <Footer />
    </div>
  );
}

function Layout() {
  const { isAuthenticated } = useContext(AuthContext);

  if (isAuthenticated != true) return <Navigate to="/login" />;

  return (
    <>
      <NavBar />
    </>
  );
}

export default App;

Login.jsx

const Login = () => {
  const [username, setUserName] = useState("");
  const [password, setPassword] = useState("");

  const navigate = useNavigate();
  const { isAuthenticated, setIsAuthenticated } = useContext(AuthContext);

  const handleLogin = () => setIsAuthenticated(true);
  const handleLogout = () => setIsAuthenticated(false);

  const updateToken = (username, password) => {
    // make axios call and check if username & password are valid
    window.localStorage.setItem("auth state", true);
    handleLogin();
    navigate("/");

    // excluded for now to test and develop
    // LoginService.authLogin({
    //   username: username,
    //   password: password,
    // }).then(() => {
    // });
  };

  return (
    <div>
      <h1>Please Log In</h1>
      {/* <form onSubmit={handleSubmit}> */}
      <label>
        <p>Username</p>
        <input
          type="text"
          onChange={(e) => setUserName(e.target.value)}
          value={username}
          required
        />
      </label>
      <br />
      <label>
        <p>Password</p>
        <input
          type="password"
          onChange={(e) => setPassword(e.target.value)}
          value={password}
          required
        />
      </label>
      <div>
        <button
          // component={Link}
          // to="/home"
          onClick={() => updateToken(username, password)}
        >
          <Link to="/">Sign In</Link>
        </button>
      </div>
      {/* </form> */}
    </div>
  );
};

问题原因及修复方案

核心问题

localStorage.getItem()返回的是字符串类型,而你在Layout组件里判断的是isAuthenticated != true(布尔值比较)。页面刷新时,stillAuth是字符串"true",不是布尔值true,所以条件isAuthenticated != true会成立,导致跳转到登录页。

修复步骤

  1. 转换localStorage值为布尔类型:初始化isAuthenticated状态时,把字符串转为布尔值:
const stillAuth = window.localStorage.getItem("auth state") === "true";
const [isAuthenticated, setIsAuthenticated] = useState(stillAuth);

这样stillAuth会是布尔值true或false,和后续状态保持类型一致。

  1. 显式存储字符串值:登录时存储字符串"true"(避免自动类型转换的潜在问题):
window.localStorage.setItem("auth state", "true");
  1. 可选:添加加载过渡状态:如果后续有异步验证(比如后端token校验),可添加加载状态避免页面闪烁:
function App() {
  const [isAuthenticated, setIsAuthenticated] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const storedAuth = window.localStorage.getItem("auth state") === "true";
    setIsAuthenticated(storedAuth);
    setLoading(false);
  }, []);

  if (loading) return <div>加载中...</div>;

  return (
    // 原渲染内容
  );
}

同时修改Layout组件的判断逻辑:

if (isAuthenticated === false) return <Navigate to="/login" />;

其他实现方案

  • 封装Context逻辑:把认证状态的读取、更新逻辑统一放到Context Provider中,避免代码散落在各个组件。
  • 自定义Hook:创建useAuth() Hook,封装登录、登出、状态获取逻辑,提升代码复用性。
  • 使用持久化库:比如use-local-storage-state,自动处理类型转换和状态同步,简化代码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 07:55:19