ReactJS中如何根据用户权限条件展示组件并配置对应路由
问题原因分析
- 状态逻辑错误:user初始值设为数组
[],未登录时赋值为false,查询用户角色的useEffect未判断user是否存在,直接读取user.uid会抛出执行错误,阻塞后续路由渲染;监听登录状态的onAuthStateChanged回调useEffect错误将user加入依赖项,会触发无限重渲染。 - 路由逻辑缺陷:未登录状态下依然会渲染管理员/普通用户的权限路由块,且公开路由放在权限路由之后,根路径路由未加
exact属性,会匹配所有路径导致其他路由无法生效。
修复后的完整代码
import React, { useEffect, useState } from "react"; import "./App.css"; import Register from "./Register"; import { auth, db } from "./firebase"; import { BrowserRouter as Router, Switch, Route } from "react-router-dom" import Login from "./Login.js"; import Inicio from "./Inicio"; import Reset from "./Reset"; import CrearEstudiante from "./CrearEstudiante"; import Estudiantes from "./ListadoEstudiantes" import Pedidos from "./ListadoPedidos" import CrearPedidos from "./CrearPedidos"; import AgregarLibro from "./AgregarLibro"; import AgregarAIB from "./AgregarAIB"; import AgregarOpcionales from "./AgregarOpcionales"; import Comprar from "./Comprar"; import VerPedidos from "./VerPedidos"; import UpdateProfile from "./UpdateProfile"; import EditForm from "./EditForm"; import DBLibros from "./DBLibros"; import DBDiccionarios from "./DBDiccionarios"; import DBLocal from "./DBLocal"; function App() { const [user, setUser] = useState(null); const [isAdmin, setIsAdmin] = useState(false); const [loading, setLoading] = useState(true); // 监听登录状态,仅注册一次 useEffect(() => { const unsubscribe = auth.onAuthStateChanged((authUser) => { setUser(authUser || null); setLoading(false); }) // 组件卸载时清理监听,避免内存泄漏 return () => unsubscribe(); }, []) // 仅当用户登录后查询管理员身份 useEffect(() => { if (!user) { setIsAdmin(false); return; } const userDoc = db.collection('usuarios').doc(user.uid); userDoc.get().then(doc => { if (doc.exists) { setIsAdmin(doc.data().isAdmin || false) } }); }, [user]) // 调试日志可按需保留 useEffect(() => { if(!user) return; if(isAdmin){ console.log("I'm an admin", isAdmin, user.uid) }else{ console.log("No admin", isAdmin, user.uid) } }, [isAdmin, user]) if(loading) return <div className="app">加载中...</div> return ( <div className="app"> <Router> <Switch> {/* 公开路由:所有用户可访问,放在最前 */} <Route path="/Register"> <Register/> </Route> <Route path="/Reset"> <Reset /> </Route> <Route exact path="/"> <Login/> </Route> {/* 仅登录用户可访问的权限路由 */} {user && isAdmin && ( <> <Route path="/Agregar_Libros"> <Inicio user={user}/> <AgregarLibro /> </Route> <Route path="/Agregar_AIB"> <Inicio user={user}/> <AgregarAIB /> </Route> <Route path="/Agregar_Opcional"> <Inicio user={user}/> <AgregarOpcionales /> </Route> <Route path="/DB_Libros"> <Inicio user={user}/> <DBLibros /> </Route> <Route path="/DB_Diccionarios"> <Inicio user={user}/> <DBDiccionarios /> </Route> <Route path="/DB_Local"> <Inicio user={user}/> <DBLocal /> </Route> </> )} {user && !isAdmin && ( <> <Route path="/Inicio"> <Inicio user={user}/> <Estudiantes user={user} /> <Pedidos user={user} /> </Route> <Route path="/Profile"> <Inicio user={user}/> <UpdateProfile user={user}/> </Route> <Route path="/Editar_Estudiante"> <Inicio user={user}/> <EditForm user={user}/> </Route> <Route path="/Crear_Estudiante"> <Inicio user={user}/> <CrearEstudiante user={user}/> </Route> <Route path="/Crear_Pedidos"> <Inicio user={user}/> <CrearPedidos user={user}/> </Route> <Route path="/Comprar"> <Inicio user={user}/> <Comprar user={user}/> </Route> <Route path="/Ver_Pedidos"> <Inicio user={user}/> <VerPedidos user={user}/> </Route> </> )} </Switch> </Router> </div> ); } export default App;
额外优化建议
- 可以为权限路由单独封装
PrivateRoute组件,简化代码逻辑,避免重复判断用户状态 - Firestore层面需要添加对应安全规则,避免未授权用户直接通过接口访问敏感数据,仅靠前端路由拦截存在安全风险
内容的提问来源于stack exchange,提问作者React Enjoyer
相关产品推荐
相关产品推荐

