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

React Router中<Navigate />组件权限重定向失效问题排查

问题排查:非Admin用户访问/home路由未触发重定向

核心问题分析

你当前的代码存在两个关键问题:

  1. 路由条件渲染逻辑错误:你只在role === "admin"时才渲染/home路由,这意味着非Admin用户访问/home时,React Router的路由表中根本没有这条规则,无法触发你写的重定向逻辑,最终会匹配到最后一个path="/"的路由,但这个路由的逻辑也不符合预期。
  2. Auth状态监听逻辑冗余:每次firebaseData变化时都会重新订阅onAuthStateChanged,可能导致重复监听和状态更新异常,同时初始状态下role为空字符串,会引发路由渲染的时序问题。

解决方案

步骤1:创建复用的Admin权限保护组件

抽离权限判断逻辑,避免重复代码:

import { Navigate } from "react-router-dom";

const ProtectedAdminRoute = ({ isAuthenticated, role, children }) => {
  // 未登录 或 已登录但非Admin,直接重定向到首页
  if (!isAuthenticated || role !== "admin") {
    return <Navigate replace to="/" />;
  }
  return children;
};

步骤2:修改App.js的路由与状态逻辑

调整路由渲染方式,优化Auth监听,并增加加载状态避免时序问题:

import "./App.css";
import React from "react";
import { Route, Routes, Navigate } from "react-router-dom";

import Landing from "./components/Landing";
import PhoneDetails from "./components/PhoneDetails";
import Home from "./components/Home/App.jsx";
import Signup from "./components/Signup";
import SignIn from "./components/Signin";
import ForgotPassword from "./components/ForgotPassword";

import { auth } from "./firebase-config.js";
import FirebaseData from "./firebaseData";

// 导入权限保护组件
const ProtectedAdminRoute = ({ isAuthenticated, role, children }) => {
  if (!isAuthenticated || role !== "admin") {
    return <Navigate replace to="/" />;
  }
  return children;
};

function App() {
  const firebaseData = FirebaseData();

  const [displayName, setDisplayName] = React.useState("");
  const [isAuthenticated, setIsAuthenticated] = React.useState(false);
  const [role, setRole] = React.useState("");
  const [isLoading, setIsLoading] = React.useState(true); // 增加加载状态

  useEffect(() => {
    // 订阅Auth状态,返回卸载时的清理函数
    const unsubscribe = auth.onAuthStateChanged((user) => {
      if (user) {
        setIsAuthenticated(true);
        setDisplayName(user.displayName);
        // 当用户存在且firebaseData已加载时,更新角色
        if (firebaseData) {
          setRole(firebaseData.users?.[user.uid]?.role);
        }
        setIsLoading(false);
      } else {
        setIsAuthenticated(false);
        setDisplayName("");
        setRole("");
        setIsLoading(false);
      }
    });

    // 组件卸载时取消订阅
    return unsubscribe;
  }, [firebaseData]);

  // 加载中状态,避免初始值导致的错误跳转
  if (isLoading) {
    return <div>Loading...</div>;
  }

  return (
    <Routes>
      <Route
        path="/"
        element={
          <Home
            isAuthenticated={isAuthenticated}
            displayName={displayName}
            role={role}
          />
        }
      />
      <Route path="/signup" element={<Signup />} />
      <Route path="/signin" element={<SignIn />} />
      <Route path="/forgot-password" element={<ForgotPassword />} />

      {/* 移除路由的条件渲染,改用权限组件包裹 */}
      <Route
        path="/home"
        element={
          <ProtectedAdminRoute isAuthenticated={isAuthenticated} role={role}>
            <Landing />
          </ProtectedAdminRoute>
        }
      />

      <Route
        path="/details"
        element={
          <ProtectedAdminRoute isAuthenticated={isAuthenticated} role={role}>
            <PhoneDetails />
          </ProtectedAdminRoute>
        }
      />

      {/* 通配符路由,匹配所有未定义的路径,统一重定向到首页 */}
      <Route path="*" element={<Navigate replace to="/" />} />
    </Routes>
  );
}

export default App;

关键修改说明

  • 路由逻辑调整:不再条件渲染/home路由,而是始终将其加入路由表,通过ProtectedAdminRoute组件内部的权限判断来控制访问权限,确保任何访问/home的请求都会经过校验。
  • 加载状态优化:增加isLoading状态避免初始状态(未获取到用户数据时)的错误跳转,提升用户体验。
  • Auth订阅优化:使用unsubscribe函数清理订阅,避免内存泄漏和重复监听。

内容的提问来源于stack exchange,提问作者002 DMAM Jigyasa Nagpal

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 04:15:52