部署Vercel后React私有路由重定向至首页问题求助
问题分析
你遇到的Vercel部署后私有路由被强制跳转到首页的问题,核心是初始加载时的状态竞态:
- 应用启动时
isAuthenticated初始值为null,ProtectedLayout会把null当成未认证状态,直接重定向到/login。 - 紧接着
AuthenticationHandler的useEffect执行,检测到sessionStorage里的bbuid后把isAuthenticated设为true,但此时路由已经被改成/login了,触发代码里的“当前是/login则跳回首页”逻辑,把你原本要访问的私有路由(比如/edit-user)覆盖了。
本地环境因为资源加载快、缓存等原因,这个竞态问题不明显,所以没暴露出来。
修复步骤
1. 给ProtectedLayout加加载状态,避免过早重定向
修改ProtectedLayout.tsx,当isAuthenticated还在null(也就是认证状态还没确定)时,不做重定向,先显示加载内容:
interface ProtectedLayoutProps { isAuthenticated: boolean | null; } const ProtectedLayout: React.FC<ProtectedLayoutProps> = ({ isAuthenticated }) => { // 认证状态未确定时,显示加载中 if (isAuthenticated === null) { return <div>加载中...</div>; // 换成你自己的加载组件也行 } if (!isAuthenticated) { return <Navigate to="/login" replace />; } return ( <div> <Outlet /> </div> ); }; export default ProtectedLayout;
2. 调整AuthenticationHandler的跳转逻辑,保留原路径
修改AuthenticationHandler.tsx,只有当用户当前在/login页面时才跳回首页,其他私有路由保持原样:
import { useEffect } from "react"; import { useNavigate } from "react-router-dom"; interface Props { setIsAuthenticated: (authState: boolean) => void; } const AuthenticationHandler: React.FC<Props> = ({ setIsAuthenticated }) => { const navigate = useNavigate(); useEffect(() => { const checkAuthentication = () => { const urlParams = new URLSearchParams(window.location.search); const bbuid = urlParams.get("bbuid"); const currentPath = window.location.pathname; if (bbuid) { sessionStorage.setItem("bbuid", bbuid); // 移除参数后跳回当前路径,不是首页 navigate(currentPath, { replace: true }); setIsAuthenticated(true); } else if (sessionStorage.getItem("bbuid")) { setIsAuthenticated(true); // 只有在/login页面时才跳回首页 if (currentPath === "/login") { navigate("/", { replace: true }); } // 其他私有路由不跳转,保持原路径 } else { setIsAuthenticated(false); // 未认证且不在/login时,才跳登录页 if (currentPath !== "/login") { navigate("/login", { replace: true }); } } }; checkAuthentication(); }, [navigate, setIsAuthenticated]); return null; }; export default AuthenticationHandler;
3. 优化初始状态,减少竞态
在App.tsx初始化isAuthenticated时,直接读取sessionStorage的值,避免一开始就处于null状态:
import { useState } from "react"; import Home from "./pages/Home"; import Auth from "./pages/Auth"; import ProtectedLayout from "./routes/ProtectedLayout"; import AuthenticationHandler from "./components/Auth/AuthenticationHandler"; import Onboarding from "./pages/Onboarding"; const App: React.FC = () => { // 初始化时直接检查sessionStorage,设置初始认证状态 const [isAuthenticated, setIsAuthenticated] = useState<boolean | null>( () => sessionStorage.getItem("bbuid") !== null ); return ( <Router> <AuthenticationHandler setIsAuthenticated={setIsAuthenticated} /> <Routes> <Route path="/login" element={<Auth />} /> <Route element={<ProtectedLayout isAuthenticated={isAuthenticated} />}> <Route path="/" element={<Home />} /> <Route path="/edit-user" element={<Onboarding />} /> </Route> </Routes> </Router> ); }; export default App;
4. 补充Vercel路由配置(防止刷新404)
在项目根目录创建vercel.json,添加重写规则,确保所有请求都指向React的入口文件:
{ "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }] }
内容的提问来源于stack exchange,提问作者krish
相关产品推荐
相关产品推荐

