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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 08:24:04