React Router中<Navigate />组件权限重定向失效问题排查
问题排查:非Admin用户访问/home路由未触发重定向
核心问题分析
你当前的代码存在两个关键问题:
- 路由条件渲染逻辑错误:你只在
role === "admin"时才渲染/home路由,这意味着非Admin用户访问/home时,React Router的路由表中根本没有这条规则,无法触发你写的重定向逻辑,最终会匹配到最后一个path="/"的路由,但这个路由的逻辑也不符合预期。 - 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
相关产品推荐
相关产品推荐

