React Router v6登录后重定向至预订页面的问题解决
React Router v6 登录后重定向至原目标页面的正确实现
问题分析
你当前的实现存在两个核心问题:
- 拦截逻辑放在
Rooms组件里不合理,/book/:roomId是独立路由,无需由Rooms组件负责路径匹配与拦截 - 登录后重定向的触发逻辑存在优先级问题,可能被AuthContext中默认的页面跳转逻辑覆盖,或者状态更新后的useEffect未正确执行
正确实现方案
1. 实现通用受保护路由组件
创建ProtectedRoute组件,统一处理未认证用户的重定向逻辑,同时完整存储目标路径:
// components/ProtectedRoute.tsx import React from 'react'; import { Navigate, useLocation } from 'react-router-dom'; import { useAuth } from '../contexts/AuthContext'; const ProtectedRoute = ({ children }: { children: React.ReactNode }) => { const { isAuthenticated } = useAuth(); const location = useLocation(); if (!isAuthenticated) { // 存储完整目标路径(含查询参数)到sessionStorage,同时通过路由state传递 const targetPath = location.pathname + location.search; sessionStorage.setItem('redirectAfterLogin', targetPath); return <Navigate to="/signin" state={{ from: location }} replace />; } return <>{children}</>; }; export default ProtectedRoute;
2. 配置路由,用ProtectedRoute保护预订页面
在路由配置文件中,将/book/:roomId路由用ProtectedRoute包裹,替代原有的分散拦截逻辑:
// App.tsx import { Routes, Route } from 'react-router-dom'; import ProtectedRoute from './components/ProtectedRoute'; import Rooms from './components/Rooms'; import BookRoom from './components/BookRoom'; // 你的预订页面组件 import SignIn from './components/SignIn'; import Dashboard from './components/Dashboard'; function App() { return ( <Routes> <Route path="/" element={<Rooms />} /> <Route path="/signin" element={<SignIn />} /> <Route path="/dashboard" element={<Dashboard />} /> {/* 用ProtectedRoute包裹需要认证的预订页面 */} <Route path="/book/:roomId" element={ <ProtectedRoute> <BookRoom /> </ProtectedRoute> } /> </Routes> ); } export default App;
3. 修改SignIn组件的重定向逻辑
优先读取路由state中的来源路径,再用sessionStorage兜底,最后设置默认跳转路径:
// components/SignIn.tsx import React, { useEffect } from 'react'; import { useNavigate, useLocation } from 'react-router-dom'; import { useAuth } from '../contexts/AuthContext'; const SignIn = () => { const navigate = useNavigate(); const location = useLocation(); const { isAuthenticated } = useAuth(); useEffect(() => { if (isAuthenticated) { // 优先级:路由state > sessionStorage > 默认仪表盘 const targetPath = location.state?.from?.pathname + location.state?.from?.search || sessionStorage.getItem('redirectAfterLogin') || '/dashboard'; // 清除存储的路径,避免重复跳转 sessionStorage.removeItem('redirectAfterLogin'); // 重定向至目标页面 navigate(targetPath, { replace: true }); } }, [isAuthenticated, navigate, location.state]); // 登录表单处理逻辑示例 const handleLogin = async (e: React.FormEvent) => { e.preventDefault(); // 调用登录API,成功后更新AuthContext的认证状态 // await loginAPI(); // auth.login(); // 假设AuthContext提供login方法更新isAuthenticated }; return ( <div> <h2>登录</h2> <form onSubmit={handleLogin}> {/* 表单输入框 */} <button type="submit">登录</button> </form> </div> ); }; export default SignIn;
4. 移除Rooms组件中的冗余逻辑
删除原Rooms组件中关于/book路径的匹配与拦截代码,回归组件本身的功能:
// components/Rooms.tsx import React from 'react'; const Rooms: React.FC = () => { return <div>Room List Here</div>; }; export default Rooms;
关键注意事项
- 确保AuthContext中的
isAuthenticated状态是响应式的,登录成功后会立即触发组件重新渲染 - 若AuthProvider中存在自动跳转至仪表盘的逻辑,需限制其触发条件(仅当用户直接访问登录页且已认证时执行)
- 存储
location.pathname + location.search可完整保留带查询参数的目标路径,比如/book/123?date=2024-05-20
内容的提问来源于stack exchange,提问作者Suhail
相关产品推荐
相关产品推荐

