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

如何为React应用配置仅登录可访问路由?MERN栈开发问题

MERN栈Facebook克隆项目路由重构问题

问题背景

  • 项目需求:未登录用户自动跳转至包含注册/登录表单的/welcome页面;登录用户可访问/、/messages、/profile和/users/:userID路由
  • 重构前:所有路由集中在App.js中,通过返回布尔值的isLoggedIn()函数控制权限,功能正常
  • 重构后:仅/和/welcome路由正常工作,登录状态下的其他路由(如/profile、/users/:userID)无法被识别

相关代码

Index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './components/app/App';
import { BrowserRouter } from 'react-router-dom';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <BrowserRouter>
    <React.StrictMode>
      <App />
    </React.StrictMode>
  </BrowserRouter>
);

App.js

import './App.css';
import React from 'react';
import {
  useNavigate,
  Routes,
  Route,
  Navigate,
} from "react-router-dom";
import { isLoggedIn } from '../../utilities/LoggedInCheck';
import LandingPage from '../../pages/LandingPage';
import AuthenticatedRoutes from './AuthenticatedRoutes';

const App = () => {
  const navigate = useNavigate();

  return (
    <Routes>
      {/* ====== 仅登录可访问路由 ======== */}
      <Route
        path='/'
        element={isLoggedIn()
          ? <AuthenticatedRoutes navigate={navigate} />
          : <Navigate to='/welcome' />
        }
      />

      {/* ====== 未登录可访问路由 ======== */}
      <Route
        path='/welcome'
        element={!isLoggedIn()
          ? <LandingPage navigate={navigate} />
          : <Navigate to='/' />
        }
      />
    </Routes>
  );
}

export default App;

AuthenticatedRoutes.js

import React, { useState, useEffect } from 'react';
import { Routes, Route } from "react-router-dom";
import { useSessionTimeOutCheck } from '../../utilities/LoggedInCheck';
import { isLoggedIn } from '../../utilities/LoggedInCheck';
import LoginPopup from '../auth/LoginPopup';
import { findUser } from '../../api_calls/usersAPI';
import Profile from '../profilepage/Profile';
import OwnProfile from '../profilepage/OwnProfile';
import Navbar from '../navbar/Navbar';
import getSessionUserID from '../../utilities/GetSessionUserID';
import Feed from '../feed/Feed';

const AuthenticatedRoutes = ({ navigate }) => {
  const [token, setToken] = useState(window.localStorage.getItem('token'));
  const sessionUserID = getSessionUserID(token);
  const [sessionUser, setSessionUser] = useState(null);

  const showLoginPopup = !useSessionTimeOutCheck();

  useEffect(() => {
    if (token && sessionUserID) {
      findUser(token, sessionUserID)
      .then(userData => {
        window.localStorage.setItem("token", userData.token)
        setToken(window.localStorage.getItem("token"))
        setSessionUser(userData.user);
        console.log(userData.user);
      })
    }
  },[])
  
  return (
    <div className='h-screen w-screen bg-#bgGrey dark:bg-gray-900 flex flex-col'>
      {/* 登录超时弹窗 */}
      {showLoginPopup && 
        <div className='z-40 absolute h-full w-full'>
          <LoginPopup navigate={navigate} />
        </div>
      }

      {/* 导航栏 */}
      <div className='z-30'>
        <Navbar navigate={navigate} token={token} setToken={setToken} 
          sessionUserID={sessionUserID} sessionUser={sessionUser} setSessionUser={sessionUser}/>
      </div>

      {/* 主页面区域 */}
      <div className='w-screen h-screen flex flex-row '>
        {/* 主内容区 */}
        <div className='w-full h-full'>
          <Routes>
            {/* 首页信息流 */}
            <Route path='/'  element={
              <Feed navigate={navigate} token={token} setToken={setToken} 
            sessionUserID={sessionUserID} sessionUser={sessionUser} setSessionUser={sessionUser}/>} />
            
            {/* 其他用户主页 */}
            <Route path="/users/:userID/"  element={ 
              <Profile navigate={navigate} token={token} setToken={setToken} 
            sessionUserID={sessionUserID} sessionUser={sessionUser} setSessionUser={sessionUser}/>}/>
            {/* 当前登录用户主页 */}
            <Route path='/profile'  element={ 
              <OwnProfile navigate={navigate} token={token} setToken={setToken} 
            sessionUserID={sessionUserID} sessionUser={sessionUser} setSessionUser={sessionUser}/>}/>

            {/* 消息页面(注释状态) */}
            {/* <Route path='/messages'  element={ 
              <MessengerPage navigate={navigate} token={token} setToken={setToken} 
              sessionUserID={sessionUserID} sessionUser={sessionUser} setSessionUser={sessionUser}/>}/> */}
          </Routes>
        </div>

        {/* 侧边栏在线好友区 */}
        <div className='flex flex-row items-center justify-between h-full sm:w-[28rem]  md:w-[30.5rem] lg:w-[34.5rem] px-4
        border-l-2'>
          MESSENGER
        </div>
      </div>
    </div>
  );
}

export default AuthenticatedRoutes;

问题原因及解决方案

核心问题

App.js中仅将AuthenticatedRoutes挂载在/路径下,当访问/profile这类子路径时,App.js的Routes无法匹配到对应的路由规则,导致这些路径被忽略。同时,AuthenticatedRoutes内部的子路由使用了绝对路径,不符合嵌套路由的匹配逻辑。

修复步骤

  1. 修改App.js中的父路由规则
    将父路由的path改为/*,确保所有以/开头的路径都能进入AuthenticatedRoutes进行匹配:

    <Route
      path='/*'
      element={isLoggedIn() ? <AuthenticatedRoutes navigate={navigate} /> : <Navigate to='/welcome' />}
    />
    
  2. 调整AuthenticatedRoutes中的子路由为相对路径
    去掉子路由路径开头的/,让路由基于父路径进行匹配:

    <Routes>
      {/* 首页信息流:使用index匹配父路径默认内容 */}
      <Route index element={
        <Feed navigate={navigate} token={token} setToken={setToken} 
      sessionUserID={sessionUserID} sessionUser={sessionUser} setSessionUser={sessionUser}/>} />
      
      {/* 其他用户主页 */}
      <Route path="users/:userID" element={ 
        <Profile navigate={navigate} token={token} setToken={setToken} 
      sessionUserID={sessionUserID} sessionUser={sessionUser} setSessionUser={sessionUser}/>}/>
      {/* 当前登录用户主页 */}
      <Route path="profile" element={ 
        <OwnProfile navigate={navigate} token={token} setToken={setToken} 
      sessionUserID={sessionUserID} sessionUser={sessionUser} setSessionUser={sessionUser}/>}/>
    
      {/* 消息页面 */}
      <Route path="messages" element={ 
        <MessengerPage navigate={navigate} token={token} setToken={setToken} 
        sessionUserID={sessionUserID} sessionUser={sessionUser} setSessionUser={sessionUser}/>}/>
    </Routes>
    

补充说明

使用index路由替代原有的path='/',可以更清晰地表示这是父路由的默认内容。相对路径的嵌套路由会自动继承父路由的路径前缀,确保路由匹配逻辑正确。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 00:52:11