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

React受保护路由刷新后重定向至根路径问题求助

React 路由刷新重定向问题

问题现象

登录后访问 /dashboard 等受保护路由,刷新页面时会被重定向至根路径 /;但刷新 /myaccount 这类非受保护路由时无此问题。

期望行为

刷新页面后保持当前路由状态,所有路由均不会被重定向至根路径 /。

已尝试操作

  • 检查了 Context 中的 token 和 userType 状态管理
  • 确认登录后 fetchData 函数能正确获取用户类型

相关代码

App.js

import React, { Suspense, lazy } from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import Layout from "./components/layout/Layout";
import { CombinedProvider } from "./contexts/useContext";
import { ToastContainer } from "react-toastify";
import ProtectedRoute from "./utils/ProtectedRoute";

const SelectionPage = lazy(() => import("./pages/SelectionPage"));
const SignupPage = lazy(() => import("./pages/Signup"));
const LoginTanod = lazy(() => import("./pages/LoginTanod"));
const LoginResident = lazy(() => import("./pages/ResidentLogin"));

// Tanod routes
const Dashboard = lazy(() => import("./components/users/tanods/Dashboard"));
const Patrolmap = lazy(() => import("./components/users/tanods/Map"));
const Equipments = lazy(() => import("./components/users/tanods/Equipment"));
const Performance = lazy(() => import("./components/users/tanods/Performance"));
const Schedule = lazy(() => import("./components/users/tanods/Schedule"));
const Incidents = lazy(() => import("./components/users/tanods/Incidents"));
const MyAccount = lazy(() => import("./components/users/tanods/MyAcc"));

// Resident routes
const ResidentRating = lazy(() => import("./components/users/residents/TanodPersonels"));
const ResidentDashboard = lazy(() => import("./components/users/residents/Dashboard"));

function App() {
  return (
    <div className="flex-1 p-6 bg-background text-text">
      <BrowserRouter>
        <CombinedProvider>
          <Suspense fallback={<div>Loading...</div>}>
            <ToastContainer />
            <Routes>
              {/* Public Routes */}
              <Route path="/" element={<SelectionPage />} />
              <Route path="/tanod-login" element={<LoginTanod />} />
              <Route path="/resident-login" element={<LoginResident />} />
              <Route path="/signup" element={<SignupPage />} />
              <Route element={<Layout />}>
              <Route path="/myaccount" element={<MyAccount />}/>
              </Route>
              {/* Protected Routes for Tanod */}
              <Route element={<Layout />}>
                <Route
                  path="/dashboard"
                  element={<ProtectedRoute userTypeAllowed={["tanod"]}><Dashboard /></ProtectedRoute>}
                />
                <Route
                  path="/patrolmap"
                  element={<ProtectedRoute userTypeAllowed={["tanod"]}><Patrolmap /></ProtectedRoute>}
                />
                <Route
                  path="/equipments"
                  element={<ProtectedRoute userTypeAllowed={["tanod"]}><Equipments /></ProtectedRoute>}
                />
                <Route
                  path="/performance"
                  element={<ProtectedRoute userTypeAllowed={["tanod"]}><Performance /></ProtectedRoute>}
                />
                <Route
                  path="/schedule"
                  element={<ProtectedRoute userTypeAllowed={["tanod"]}><Schedule /></ProtectedRoute>}
                />
                <Route
                  path="/incidents"
                  element={<ProtectedRoute userTypeAllowed={["tanod"]}><Incidents /></ProtectedRoute>}
                />
                
              </Route>

              {/* Protected Routes for Residents */}
              <Route element={<Layout />}>
                <Route
                  path="/resident-dashboard"
                  element={<ProtectedRoute userTypeAllowed={["resident"]}><ResidentDashboard /></ProtectedRoute>}
                />
                <Route
                  path="/ratetanod"
                  element={<ProtectedRoute userTypeAllowed={["resident"]}><ResidentRating /></ProtectedRoute>}
                />
              </Route>
            </Routes>
          </Suspense>
        </CombinedProvider>
      </BrowserRouter>
    </div>
  );
}

export default App;

ProtectedRoute.js

import React from "react";
import { Navigate } from "react-router-dom";
import { useCombinedContext } from "../contexts/useContext";

export default function ProtectedRoute({ userTypeAllowed, children }) {
  const { userType, token } = useCombinedContext();

  if (!token || !userTypeAllowed.includes(userType)) {
    return <Navigate to="/" />;
  }
  
  return children;
}

useContext.js

import React, { createContext, useState, useEffect, useContext, useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import { toast } from 'react-toastify'; 

export const CombinedContext = createContext();

export const CombinedProvider = ({ children }) => {
  const [token, setToken] = useState(localStorage.getItem('token'));
  const [userType, setUserType] = useState(null); 
  const navigate = useNavigate();

  const fetchData = useCallback(async () => {
    // Fetch user data to get userType
  }, []);

  useEffect(() => {
    if (token) {
      fetchData();
    }
  }, [token, fetchData]);

  const logout = () => {
    localStorage.removeItem('token');
    setToken(null);
    setUserType(null);
    navigate('/');
  };

  return (
    <CombinedContext.Provider value={{ token, userType, logout }}>
      {children}
    </CombinedContext.Provider>
  );
};

export const useCombinedContext = () => useContext(CombinedContext);

问题原因

刷新页面时,Context 初始化阶段 userType 初始值为 null,而 ProtectedRoute 会立即检查 userType 是否在允许列表中。此时 fetchData 还未完成异步请求,userType 仍为 null,触发重定向逻辑。

解决办法

1. 添加加载状态,避免过早重定向

在 CombinedProvider 中添加 isLoading 状态,标记用户数据是否加载完成:

修改 useContext.js:

import React, { createContext, useState, useEffect, useContext, useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import { toast } from 'react-toastify'; 

export const CombinedContext = createContext();

export const CombinedProvider = ({ children }) => {
  const [token, setToken] = useState(localStorage.getItem('token'));
  const [userType, setUserType] = useState(null); 
  const [isLoading, setIsLoading] = useState(true); // 添加加载状态
  const navigate = useNavigate();

  const fetchData = useCallback(async () => {
    setIsLoading(true);
    try {
      // 补充实际的接口请求逻辑
      const response = await fetch('/api/user-profile', {
        headers: { Authorization: `Bearer ${token}` }
      });
      const data = await response.json();
      setUserType(data.userType);
    } catch (error) {
      toast.error('获取用户信息失败');
      logout();
    } finally {
      setIsLoading(false);
    }
  }, [token]);

  useEffect(() => {
    if (token) {
      fetchData();
    } else {
      setIsLoading(false);
    }
  }, [token, fetchData]);

  const logout = () => {
    localStorage.removeItem('token');
    setToken(null);
    setUserType(null);
    navigate('/');
  };

  return (
    <CombinedContext.Provider value={{ token, userType, logout, isLoading }}>
      {children}
    </CombinedContext.Provider>
  );
};

export const useCombinedContext = () => useContext(CombinedContext);

2. 修改 ProtectedRoute,等待加载完成再判断

在 ProtectedRoute 中,当 isLoading 为 true 时,返回加载组件,避免立即重定向:

修改 ProtectedRoute.js:

import React from "react";
import { Navigate } from "react-router-dom";
import { useCombinedContext } from "../contexts/useContext";

export default function ProtectedRoute({ userTypeAllowed, children }) {
  const { userType, token, isLoading } = useCombinedContext();

  if (isLoading) {
    return <div>Loading...</div>; // 或自定义加载组件
  }

  if (!token || !userTypeAllowed.includes(userType)) {
    return <Navigate to="/" />;
  }
  
  return children;
}

3. 优化:从 localStorage 直接读取 userType(可选)

如果登录时将 userType 也存入 localStorage,可以避免每次刷新都请求接口:

登录成功后:

localStorage.setItem('token', 'your-token');
localStorage.setItem('userType', 'tanod'); // 或 'resident'

修改 useContext.js 的初始化:

const [userType, setUserType] = useState(localStorage.getItem('userType'));

这样刷新时 userType 会直接从 localStorage 获取,无需等待异步请求完成,进一步优化体验。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 01:05:56