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

React Router v6单页应用认证后如何加载正确组件?

React Router 6 认证状态变更后根组件渲染的最佳实践

需求明确

需要根据用户认证状态在根路径/加载对应组件:

  • 未认证:加载<Login />
  • 用户角色为user:加载<Home />
  • 用户角色为admin:加载<Admin />

当前问题分析

  1. 认证状态更新异步,初始状态处理不当导致空白页面
  2. 登录后导航方式错误,Promise回调中返回<Navigate>无法触发路由跳转
  3. 用户对象缺少role字段,路由判断逻辑可能失效
  4. 强制刷新location.reload()引发无限循环

解决方案

1. 修复AuthContext状态管理

区分「初始化中」「未登录」「已登录」三种状态,确保用户角色正确加载:

import { useState, useEffect, createContext } from "react";
import { auth } from "./firebaseInit";

export const AuthContext = createContext();

export const AuthContextProvider = ({children}) => {
  // undefined = 认证状态初始化中;null = 未登录;对象 = 已登录用户
  const [user, setUser] = useState(undefined);

  useEffect( () => {
    const unsubscribe = auth.onAuthStateChanged( async (auth_user) => {
      if (auth_user) {
        if (auth_user.emailVerified) {
          try {
            // 调用API获取用户角色信息
            const userData = await fetch(`/api/user/${auth_user.uid}`).then(res => res.json());
            // 合并Firebase认证信息与用户角色
            setUser({...auth_user, role: userData.role });
          } catch (err) {
            console.error("获取用户信息失败:", err);
            setUser(null);
          }
        } else {
          // 未验证邮箱,视为未登录
          setUser(null);
        }
      } else {
        setUser(null);
      }
    });

    return () => unsubscribe();
  }, []);

  return (
    <AuthContext.Provider value={{user}}>
      {children}
    </AuthContext.Provider>
  );
};

2. 正确处理登录后的导航

使用React Router的useNavigate钩子触发跳转,替代无效的组件返回方式:

import { useNavigate } from "react-router-dom";
import { signInWithEmailAndPassword } from "firebase/auth";
import { auth } from "../../backend/firebaseInit";
import { Container, Box } from "@mui/material";

export default function Login() {
  const navigate = useNavigate();

  const handleSubmit = async (e) => {
    e.preventDefault();
    const data = new FormData(e.currentTarget);
    
    try {
      const userCredential = await signInWithEmailAndPassword(
        auth, 
        data.get('email'), 
        data.get('password')
      );
      const auth_user = userCredential.user;
      
      if (auth_user.emailVerified) {
        // 登录成功后跳转根路径,AuthContext会自动更新状态触发路由重渲染
        navigate("/", { replace: true });
      } else {
        alert("请先验证你的邮箱");
      }
    } catch (err) {
      console.log(err.toString());
    }
  };

  return (
    <Container>
      <Box>
        <form onSubmit={handleSubmit}>
          <input type="email" name="email" placeholder="邮箱" required />
          <input type="password" name="password" placeholder="密码" required />
          <button type="submit">登录</button>
        </form>
      </Box>
    </Container>
  );
}

3. 优化路由判断逻辑

处理初始化状态,避免空白页面,简化条件判断:

import { AuthContext } from './AuthContext';
import { useContext } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';

export default function ConvuRouter() {
  const { user } = useContext(AuthContext);

  // 认证状态初始化中,显示加载提示
  if (user === undefined) {
    return <div>加载中...</div>;
  }

  return (
    <BrowserRouter>
      <Routes>
        <Route 
          path="/" 
          element={
            !user ? <Login /> :
            user.role === 'admin' ? <Admin /> :
            user.role === 'user' ? <Home /> :
            <Login /> // 角色未知时返回登录页
          } 
        />
      </Routes>
    </BrowserRouter>
  );
};

核心要点

  • 状态阶段区分:用undefined标记初始化状态,避免把加载状态误判为未登录
  • 导航方式正确:使用useNavigate触发路由跳转,而非在Promise回调中返回组件
  • 确保角色存在:在AuthContext中完整加载用户角色信息,避免路由判断逻辑失效
  • 拒绝强制刷新:依赖React状态更新自动触发路由重渲染,杜绝location.reload()引发的无限循环

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 10:32:03