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

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里写条件判断。

修改步骤

  1. 创建ProtectedRoute组件,处理登录状态校验(未登录则重定向到登录页)
  2. 创建PublicRoute组件,处理已登录用户访问登录/注册页的重定向(已登录则跳转到首页)
  3. 在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 02:23:14