Skip to content

React Router v6 路由错误:Route 必须作为 Routes 的子元素

问题描述

许多开发者在升级到 React Router v6 后遇到了以下错误:

Error: A <Route> is only ever to be used as the child of <Routes> element, never rendered directly. Please wrap your Route in a Routes.

这个错误通常发生在从 React Router v5 迁移到 v6 时,因为两个版本之间存在重大的 API 变更。

典型的错误代码示例如下:

jsx
import { Route } from "react-router-dom";
import Welcome from "./Pages/Welcome";
import Game from "./Pages/Game";
import Leaderboard from "./Pages/Leaderboard";

function App() {
    return (
        <div>
            <Route path="/welcome">
                <Welcome />
            </Route>
            <Route path="/game">
                <Game />
            </Route>
            <Route path="/leaderboard">
                <Leaderboard />
            </Route>
        </div>
    );
}

export default App;

关键变更

React Router v6 引入了重大变更,不再支持 v5 的路由定义方式,导致上述代码无法正常工作。

解决方案

方案一:更新到 React Router v6 的正确语法

这是推荐的做法,让你的代码与最新版本保持一致:

jsx
import { Routes, Route } from "react-router-dom";
import Welcome from "./Pages/Welcome";
import Game from "./Pages/Game";
import Leaderboard from "./Pages/Leaderboard";

function App() {
    return (
        <div>
            <Routes>
                <Route path="/welcome" element={<Welcome />} />
                <Route path="/game" element={<Game />} />
                <Route path="/leaderboard" element={<Leaderboard />} />
            </Routes>
        </div>
    );
}

export default App;

同时,确保你的 index.js 文件正确设置了 BrowserRouter

jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import App from "./App";

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <BrowserRouter>
        <App />
    </BrowserRouter>
);

方案二:使用 useRoutes Hook 进行路由配置

React Router v6 还提供了基于对象的配置方式:

jsx
import { useRoutes } from "react-router-dom";
import Welcome from "./Pages/Welcome";
import Game from "./Pages/Game";
import Leaderboard from "./Pages/Leaderboard";

function App() {
    const routes = useRoutes([
        { path: "/welcome", element: <Welcome /> },
        { path: "/game", element: <Game /> },
        { path: "/leaderboard", element: <Leaderboard /> }
    ]);
    
    return (
        <div>
            {routes}
        </div>
    );
}

export default App;

方案三:降级到 React Router v5(不推荐)

如果你暂时无法适应 v6 的变化,可以考虑降级到 v5:

bash
npm install react-router-dom@5.3.0

注意

降级只是临时解决方案,React Router 团队会持续维护 v6,建议尽快迁移到新版本。

主要变更说明

React Router v6 引入了以下几项重大变更:

  1. Routes 代替 Switch:使用 <Routes> 替代了 v5 中的 <Switch> 组件
  2. element 属性代替 component:使用 element={<Component />} 替代了 component={Component} 和渲染子元素的方式
  3. 相对路由和链接:改进了相对路径的处理方式
  4. 自动路由排名:不再需要 exact 属性,路由自动选择最匹配的路径

完整示例

以下是一个使用 React Router v6 的完整应用示例:

jsx
import { Routes, Route } from "react-router-dom";
import Nav from "./components/Nav";
import Home from "./components/Home";
import About from "./components/About";
import Contact from "./components/Contact";

function App() {
  return (
    <>
      <Nav />
      <main>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/about" element={<About />} />
          <Route path="/contact" element={<Contact />} />
        </Routes>
      </main>
    </>
  );
}

export default App;
jsx
import { Link } from "react-router-dom";

function Nav() {
  return (
    <nav>
      <Link to="/">Home</Link>
      <Link to="/about">About</Link>
      <Link to="/contact">Contact</Link>
    </nav>
  );
}

export default Nav;
jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>
);

常见问题解答

为什么我的嵌套路由不工作?

在 v6 中,嵌套路由需要在父路由组件中使用 <Outlet /> 组件来渲染子路由:

jsx
import { Outlet } from "react-router-dom";

function Layout() {
  return (
    <div>
      <Header />
      <Outlet /> {/* 子路由将在这里渲染 */}
      <Footer />
    </div>
  );
}

如何实现编程式导航?

在 v6 中,使用 useNavigate Hook 替代了 useHistory

jsx
import { useNavigate } from "react-router-dom";

function MyComponent() {
  const navigate = useNavigate();
  
  const handleClick = () => {
    navigate("/target-path");
  };
  
  return <button onClick={handleClick}>Go to target</button>;
}

总结

React Router v6 带来了更简洁、更强大的路由解决方案,虽然需要一些学习成本,但新API设计更加一致和直观。遵循本文的解决方案,你应该能够解决 "Route必须作为Routes的子元素" 的错误,并顺利迁移到 v6 版本。

如需了解更多细节,请参考 React Router 官方文档