react-router-dom v6路由匹配失败:嵌套路由迁移后无法正常工作
React Router v6 嵌套路由迁移问题:"No routes match location" 错误
迁移至react-router-dom v6后,原v5中正常运行的组件嵌套路由无法工作,始终提示"No routes match location"。希望在Layout组件中配置路由,触发时加载内部路由,但未成功,此前在v5中使用Switch和BrowserRouter一切正常,无法定位问题。
原始代码
App.js
import React from "react"; import RootRouters from "./components/routers/RootRouters"; import { BrowserRouter as Router } from "react-router-dom"; import "./App.css"; function App() { return ( <Router> <div className="App"> <RootRouters /> </div> </Router> ); } export default App;
RootRouters.js
import React from "react"; import { Route, Routes } from "react-router-dom"; import Login from "../auth-page/login"; import Home from "../home/home"; import SignUp from "../auth-page/sign-up"; import AdminLogin from "../../pages/admin/Login"; import Layout from "../../components/admin/layout/Layout"; const RootRouters = () => { return ( <Routes> <Route path="/home" element={<Home />} /> <Route path="/login" element={<Login />} /> {/* <Route path="/" element={<Home />} /> */} <Route path="/register" element={<SignUp />} /> <Route path="/admin/login" element={<AdminLogin />} /> <Route element={<Layout />} /> </Routes> ); }; export default RootRouters;
Layout.js
import React, { useEffect } from "react"; import "./layout.css"; import Sidebar from "../sidebar/Sidebar"; import TopNav from "../topnav/TopNav"; import AdminRoutes from "../../routers/AdminRouters"; import { BrowserRouter, Route } from "react-router-dom"; import { useSelector, useDispatch } from "react-redux"; import ThemeAction from "../../../redux/actions/ThemeAction"; const Layout = () => { const themeReducer = useSelector((state) => state.ThemeReducer); const dispatch = useDispatch(); useEffect(() => { const themeClass = localStorage.getItem("themeMode", "theme-mode-light"); const colorClass = localStorage.getItem("colorMode", "theme-mode-light"); dispatch(ThemeAction.setMode(themeClass)); dispatch(ThemeAction.setColor(colorClass)); }, [dispatch]); return ( <Route render={(props) => ( <div className={`layout ${themeReducer.mode} ${themeReducer.color}`}> <Sidebar {...props} /> <div className="layout__content"> <TopNav /> <div className="layout__content-main"> <AdminRoutes /> </div> </div> </div> )} /> ); }; export default Layout;
AdminRoutes.js
import React from "react"; import { Route, Routes } from "react-router-dom"; import Dashboard from "../../pages/admin/Dashboard"; import Customers from "../../pages/admin/Customers"; import Products from "../../pages/admin/Products"; import NewDocument from "../../pages/admin/document/NewDocument"; import DetailDocument from "../../pages/admin/document/DetailDocument"; import EditDocument from "../../pages/admin/document/EditDocument"; const AdminRoutes = () => { return ( <Route> <Route path="/admin/documents/new" component={NewDocument} /> <Route path="/admin/documents/detail" component={DetailDocument} /> <Route path="/admin/documents/update" component={EditDocument} /> <Route path="/admin/customers" component={Customers} /> <Route path="/admin/products" component={Products} /> <Route path="/home" component={Dashboard} /> </Route> ); }; export default AdminRoutes;
问题分析及修正方案
React Router v6 对路由写法做了重大调整,你的代码存在以下核心问题:
- 废弃
component/render属性:v6中Route不再支持component和render,必须使用element属性传递组件实例。 - 嵌套路由写法变更:v6需要通过父路由的
children定义子路由,且父路由需设置匹配子路由的路径(比如/admin/*),内部用<Outlet />渲染子路由内容。 - Layout组件错误包裹
Route:Layout作为布局组件,不需要嵌套Route,直接返回布局结构即可。 - RootRouters中Layout路由未设置路径:原代码中Layout的
Route没有path,无法匹配任何admin相关路径,导致路由不生效。
修正后的代码
1. RootRouters.js
将Layout路由设置为匹配/admin路径,嵌套定义所有admin子路由:
import React from "react"; import { Route, Routes } from "react-router-dom"; import Login from "../auth-page/login"; import Home from "../home/home"; import SignUp from "../auth-page/sign-up"; import AdminLogin from "../../pages/admin/Login"; import Layout from "../../components/admin/layout/Layout"; import Dashboard from "../../pages/admin/Dashboard"; import Customers from "../../pages/admin/Customers"; import Products from "../../pages/admin/Products"; import NewDocument from "../../pages/admin/document/NewDocument"; import DetailDocument from "../../pages/admin/document/DetailDocument"; import EditDocument from "../../pages/admin/document/EditDocument"; const RootRouters = () => { return ( <Routes> <Route path="/home" element={<Home />} /> <Route path="/login" element={<Login />} /> <Route path="/register" element={<SignUp />} /> <Route path="/admin/login" element={<AdminLogin />} /> {/* 嵌套路由:父路由匹配/admin,子路由用相对路径定义 */} <Route path="/admin" element={<Layout />}> <Route index element={<Dashboard />} /> {/* 默认加载Dashboard */} <Route path="customers" element={<Customers />} /> <Route path="products" element={<Products />} /> <Route path="documents/new" element={<NewDocument />} /> <Route path="documents/detail" element={<DetailDocument />} /> <Route path="documents/update" element={<EditDocument />} /> </Route> </Routes> ); }; export default RootRouters;
2. Layout.js
移除包裹的Route,使用<Outlet />渲染子路由内容:
import React, { useEffect } from "react"; import "./layout.css"; import Sidebar from "../sidebar/Sidebar"; import TopNav from "../topnav/TopNav"; import { Outlet } from "react-router-dom"; import { useSelector, useDispatch } from "react-redux"; import ThemeAction from "../../../redux/actions/ThemeAction"; const Layout = () => { const themeReducer = useSelector((state) => state.ThemeReducer); const dispatch = useDispatch(); useEffect(() => { const themeClass = localStorage.getItem("themeMode") || "theme-mode-light"; const colorClass = localStorage.getItem("colorMode") || "theme-mode-light"; dispatch(ThemeAction.setMode(themeClass)); dispatch(ThemeAction.setColor(colorClass)); }, [dispatch]); return ( <div className={`layout ${themeReducer.mode} ${themeReducer.color}`}> <Sidebar /> <div className="layout__content"> <TopNav /> <div className="layout__content-main"> {/* Outlet专门用于渲染父路由下的子路由内容 */} <Outlet /> </div> </div> </div> ); }; export default Layout;
3. 保留AdminRoutes的写法(可选)
如果想继续使用AdminRoutes组件,修改如下:
import React from "react"; import { Route, Routes } from "react-router-dom"; import Dashboard from "../../pages/admin/Dashboard"; import Customers from "../../pages/admin/Customers"; import Products from "../../pages/admin/Products"; import NewDocument from "../../pages/admin/document/NewDocument"; import DetailDocument from "../../pages/admin/document/DetailDocument"; import EditDocument from "../../pages/admin/document/EditDocument"; const AdminRoutes = () => { return ( <Routes> <Route index element={<Dashboard />} /> <Route path="customers" element={<Customers />} /> <Route path="products" element={<Products />} /> <Route path="documents/new" element={<NewDocument />} /> <Route path="documents/detail" element={<DetailDocument />} /> <Route path="documents/update" element={<EditDocument />} /> </Routes> ); }; export default AdminRoutes;
此时RootRouters中Layout的路由改为:
<Route path="/admin/*" element={<Layout />} />
Layout中替换<Outlet />为<AdminRoutes />即可。
关键说明
- 父路由路径末尾加
*表示匹配该路径下的所有子路由(适用于单独抽离子路由组件的场景);直接嵌套子路由时,父路由写/admin,子路由用相对路径即可。 <Outlet />是v6中专门用于渲染子路由内容的组件,替代了v5中嵌套Route的写法。- 所有路由必须通过
element属性传递组件实例,比如element={<Dashboard />},而非component={Dashboard}。
内容的提问来源于stack exchange,提问作者Văn Thuận Nguyễn
相关产品推荐
相关产品推荐

