React Router v7使用createBrowserRouter注册后跳转异常问题
问题描述
我使用React Router v7、Mobx和Mobx-react构建React应用,在Signup页面点击注册按钮后,明明已经正确更新了用户状态,应用却没有跳转到首页(/),反而跳转到了登录页(/login)。改用<BrowserRouter>+<Routes>的配置后问题解决,但我想继续使用createBrowserRouter,请问原因是什么?该如何解决?
现有代码配置
App.tsx(原问题版本)
import { BrowserRouter, createBrowserRouter, Navigate, Route, RouterProvider, Routes } from 'react-router' import './App.css' import userStore from './userStore'; import Home from './Home'; import Signup from './Signup'; import Login from './Login'; import { observer } from 'mobx-react'; const router = createBrowserRouter( [ { path: "/", element: userStore.user ? <Home /> : <Navigate to='/login' />, }, { path: "/signup", element: userStore.user ? <Navigate to='/' /> : <Signup />, }, { path: "/login", element: userStore.user ? <Navigate to='/' /> : <Login />, } ] ); function App() { return ( <> <RouterProvider router={router} /> </> ) } export default observer(App);
Home.tsx
import React from 'react' import userStore from './userStore'; import { useNavigate } from 'react-router'; import { observer } from 'mobx-react'; const Home = () => { const navigate = useNavigate(); const handleLogout = ()=>{ userStore.setUser(null); navigate('/login'); }; return ( <> <div>Home</div> <button onClick={()=>handleLogout()}>logout</button> </> ) } export default observer(Home)
Signup.tsx
import React from 'react' import userStore from './userStore'; import { useNavigate } from 'react-router'; import { observer } from 'mobx-react'; const Signup = () => { const navigate = useNavigate(); const handleSignup = ()=>{ userStore.setUser('user'); navigate('/'); }; return ( <> <div>Signup page</div> <button onClick={()=>handleSignup()}>Signup</button> </> ) } export default observer(Signup)
Login.tsx
import React from 'react' const Login = () => { return ( <div>Login</div> ) } export default Login
userStore.ts
import { makeAutoObservable } from "mobx"; class UserStore{ user:string|null = null; constructor(){ makeAutoObservable(this); this.getUserFromLocalStorage(); } setUser(user:string|null){ this.user = user; if(user){ localStorage.setItem('user', user); }else{ localStorage.removeItem('user'); } } private getUserFromLocalStorage(){ const user = localStorage.getItem('user') if(user){ this.user = user; } }; }; const userStore = new UserStore(); export default userStore;
问题原因
createBrowserRouter是一次性创建路由配置的:初始化时它会读取userStore.user的初始值(null),并把这个值固化到路由规则的element属性中。后续userStore.user更新时,路由配置已经创建完成,不会重新计算element的条件判断结果。所以当你注册后跳转到/,路由还是按照初始化时的规则(user为null)重定向到/login。
而<BrowserRouter>+<Routes>的方式是在组件渲染流程中动态计算路由的element:因为App组件被observer包裹,当userStore.user变化时,App会重新渲染,路由规则也会跟着重新计算,因此能正确跳转到首页。
解决方案
要让createBrowserRouter响应Mobx状态变化,需要把权限判断逻辑从路由配置中抽离,放到路由元素内部,用组件来处理重定向,而非直接在路由配置的element里写条件判断。
修改步骤
- 创建
ProtectedRoute组件,处理登录状态校验(未登录则重定向到登录页) - 创建
PublicRoute组件,处理已登录用户访问登录/注册页的重定向(已登录则跳转到首页) - 在
createBrowserRouter配置中使用这两个组件,将条件判断逻辑交给组件内部处理
修改后的代码
ProtectedRoute.tsx
import { Navigate, Outlet } from 'react-router'; import userStore from './userStore'; import { observer } from 'mobx-react'; const ProtectedRoute = observer(() => { if (!userStore.user) { return <Navigate to="/login" replace />; } return <Outlet />; }); export default ProtectedRoute;
PublicRoute.tsx
import { Navigate, Outlet } from 'react-router'; import userStore from './userStore'; import { observer } from 'mobx-react'; const PublicRoute = observer(() => { if (userStore.user) { return <Navigate to="/" replace />; } return <Outlet />; }); export default PublicRoute;
修改后的App.tsx
import { createBrowserRouter, RouterProvider, Route } from 'react-router' import './App.css' import Home from './Home'; import Signup from './Signup'; import Login from './Login'; import ProtectedRoute from './ProtectedRoute'; import PublicRoute from './PublicRoute'; const router = createBrowserRouter([ { path: "/", element: <ProtectedRoute />, children: [ { path: "/", element: <Home /> } ] }, { path: "/signup", element: <PublicRoute />, children: [ { path: "/signup", element: <Signup /> } ] }, { path: "/login", element: <PublicRoute />, children: [ { path: "/login", element: <Login /> } ] } ]); function App() { return ( <> <RouterProvider router={router} /> </> ) } export default App;
其他文件说明
Home.tsx、Signup.tsx、Login.tsx、userStore.ts保持原有代码即可,无需修改。
内容的提问来源于stack exchange,提问作者victor cheng
相关产品推荐
相关产品推荐

