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

React Router Dom 受保护路由刷新后异常重定向问题求助

解决方案与思路

问题根源

刷新页面时Redux的认证状态尚未初始化完成,ProtectedRoute组件会先误判用户未认证并跳转到/login;等状态初始化完成后,登录页又跳转到默认的/profile,而非用户原本访问的/profile/edit。另外改用loader方式失效,大概率是没处理状态加载等待逻辑,或未正确传递原路径。


方案一:优化基于Element的ProtectedRoute组件

核心改动

  1. 等待认证状态初始化完成后再做路由判断,避免初始误判
  2. 跳转登录页时保存原访问路径,登录后自动跳回

代码实现

ProtectedRoute 组件

import { Navigate, useLocation } from 'react-router-dom';
import { useSelector } from 'react-redux';

const ProtectedRoute = ({ children }) => {
  const { isAuthenticated, isLoading } = useSelector(state => state.auth);
  const location = useLocation();

  // 状态加载中时,返回加载占位(如Spinner),不直接重定向
  if (isLoading) {
    return <div>加载中...</div>;
  }

  if (!isAuthenticated) {
    // 保存当前路径到state,供登录后跳转使用
    return <Navigate to="/login" state={{ from: location }} replace />;
  }

  return children;
};

登录页处理跳转逻辑

import { useNavigate, useLocation } from 'react-router-dom';
import { useDispatch } from 'react-redux';

const LoginPage = () => {
  const navigate = useNavigate();
  const location = useLocation();
  const dispatch = useDispatch();

  const handleLogin = async (credentials) => {
    // 执行登录逻辑
    await dispatch(login(credentials));
    // 获取原路径,默认 fallback 到 /profile
    const from = location.state?.from?.pathname || '/profile';
    navigate(from, { replace: true });
  };

  return (
    // 登录表单...
  );
};

同步Redux状态初始化逻辑

在auth slice中确保初始状态包含isLoading: true,并在初始化认证(如从localStorage读取token验证)时,先设置isLoading: true,验证完成后更新isAuthenticated并将isLoading设为false:

const authSlice = createSlice({
  name: 'auth',
  initialState: {
    isAuthenticated: false,
    isLoading: true, // 初始设为加载中
    user: null,
  },
  reducers: {
    // ...其他reducer
    setAuthLoading: (state, action) => {
      state.isLoading = action.payload;
    },
    setAuthenticated: (state, action) => {
      state.isAuthenticated = action.payload;
      state.isLoading = false;
    },
  },
});

方案二:修复Loader方式的受保护路由

核心改动

  1. 在loader中等待Redux认证状态初始化完成
  2. 未认证时重定向到登录页并携带原路径
  3. 认证通过后正常放行

代码实现

通用protectedLoader

import { redirect } from 'react-router-dom';
import { store } from '../store';

export async function protectedLoader() {
  const state = store.getState();
  const { isAuthenticated, isLoading } = state.auth;

  // 等待认证状态加载完成
  if (isLoading) {
    await new Promise(resolve => {
      const unsubscribe = store.subscribe(() => {
        const newState = store.getState();
        if (!newState.auth.isLoading) {
          unsubscribe();
          resolve();
        }
      });
    });
  }

  const updatedState = store.getState();
  if (!updatedState.auth.isAuthenticated) {
    // 携带原路径到登录页
    return redirect(`/login?from=${window.location.pathname}`);
  }

  return null;
}

路由配置中使用loader

import { createBrowserRouter } from 'react-router-dom';
import { protectedLoader } from './loaders';
import ProfileEdit from './pages/ProfileEdit';
import Profile from './pages/Profile';
import Login from './pages/Login';

const router = createBrowserRouter([
  {
    path: '/profile/edit',
    element: <ProfileEdit />,
    loader: protectedLoader,
  },
  {
    path: '/profile',
    element: <Profile />,
    loader: protectedLoader,
  },
  {
    path: '/login',
    element: <Login />,
  },
]);

登录页处理跳转

import { useNavigate, useSearchParams } from 'react-router-dom';
import { useDispatch } from 'react-redux';

const LoginPage = () => {
  const navigate = useNavigate();
  const [searchParams] = useSearchParams();
  const dispatch = useDispatch();

  const handleLogin = async (credentials) => {
    await dispatch(login(credentials));
    // 从URL参数获取原路径
    const from = searchParams.get('from') || '/profile';
    navigate(from, { replace: true });
  };

  return (
    // 登录表单...
  );
};

关键注意点

  • 必须等待认证状态初始化完成后再做路由判断,避免初始状态的误判
  • 跳转登录页时一定要携带用户原本的访问路径,登录后跳回该路径
  • Redux初始化逻辑要正确:启动时检查本地存储的token,验证过程中保持isLoading: true,完成后更新状态

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 07:42:51