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

React17+react-router-dom@5.2.0如何给子路由传默认布局组件

问题描述

我制作了一个AdminDefaultLayout管理员默认布局组件,希望将其应用于所有管理员路由组件。但由于使用React 17和react-router-dom@5.2.0版本,无法使用react-router-dom@6+中的Outlet特性。尝试相关写法后,页面仅显示AdminDefaultLayout组件,Dashboard组件不显示,请求指导解决。

期望写法

Route.js 期望结构

<BrowserRouter>
 <Routes>
  <Route path="/admin/dashboard" element={AdminDefaultLayout}>
   <Route path="/admin/dashboard" element={Dashboard} />
   <Route path="/admin/category" element={Category} />
  </Route>
 </Routes>
</BrowserRouter>

AdminDefaultLayout.jsx 期望结构

<>
<div className="container">
 <Header />
 <Sidebar />
 <main>
  <Outlet />
 </main>
 <Footer />
</div>
</>

当前环境依赖

{"name": "magicbox","version": "1.3.0","private": true,"dependencies": {"axios": "^1.5.1","google-map-react": "^1.1.7","http-proxy-middleware": "^0.19.2","isomorphic-fetch": "^3.0.0","lax.js": "^1.2.5","react": "^17.0.2","react-accessible-accordion": "^3.3.4","react-countup": "^4.4.0","react-dom": "^17.0.2","react-modal-video": "^1.2.8","react-owl-carousel3": "^2.2.5","react-router-dom": "^5.2.0","react-scripts": "^5.0.1","react-visibility-sensor": "^5.1.1","sass": "^1.51.0"}}

当前代码写法

route.jsx

import React from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import HomeFour from "./components/pages/HomeFour";
import Login from "./components/pages/Login";
import AdminDefaultLayout from "./components/AdminDefaultLayout";
import Dashboard from "./components/pages/admin/Dashboard";
const renderNavigation = () => {
  if (!(window.location.pathname === '/login' || 
        window.location.pathname === '/signup' || 
        window.location.pathname === '/coming-soon' || 
        window.location.pathname === '/error-404' || 
        window.location.pathname === '/admin/dashboard'|| 
        window.location.pathname === '/admin/category')
    ) {
        return <Navigation />;
      }
}
const AppRouter = () => {
 const existToken = localStorage.getItem('ACCESS_TOKEN');
 return (
  <>
    { existToken ? 
      (
       <Router>
        <Switch>
         <Route path="/admin/dashboard" render={(props) => <AdminDefaultLayout><Dashboard {...props} /></AdminDefaultLayout>}/>
        </Switch>
       </Router>
      ) : (
       <Router>
       {renderNavigation()}
        <Switch>
         <Route path="/login" exact component={Login} />
         <Route path="/" exact component={HomeFour} />
        </Switch>
       </Router>
       ) 
    }
   </>
  )
}
export default AppRouter;

AdminDefaultLayout.jsx

<>
 <div className="container">
  <Header />
  <Sidebar />
  <main>
    <Switch></Switch>
  </main>
  <Footer />
 </div>
</>

注:已修正错误,package.json中无react-router依赖。


解决方案

在react-router-dom@5.x中没有Outlet,可以通过以下两种方式实现布局嵌套:

方法一:利用组件children属性传递内容

这是最直接的方式,修改AdminDefaultLayout接收并渲染子组件:

1. 修改AdminDefaultLayout.jsx

import React from 'react';
import Header from './Header'; // 确保路径正确
import Sidebar from './Sidebar';
import Footer from './Footer';

const AdminDefaultLayout = ({ children }) => {
  return (
    <>
      <div className="container">
        <Header />
        <Sidebar />
        <main>
          {children} {/* 替代Outlet渲染子组件 */}
        </main>
        <Footer />
      </div>
    </>
  );
};

export default AdminDefaultLayout;

2. 调整路由写法

将需要嵌套的组件作为AdminDefaultLayout的子元素传入,同时扩展管理员路由:

import React from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import HomeFour from "./components/pages/HomeFour";
import Login from "./components/pages/Login";
import AdminDefaultLayout from "./components/AdminDefaultLayout";
import Dashboard from "./components/pages/admin/Dashboard";
import Category from "./components/pages/admin/Category"; // 导入Category组件
import Navigation from "./components/Navigation"; // 确保导入Navigation

const renderNavigation = () => {
  const excludedPaths = ['/login', '/signup', '/coming-soon', '/error-404', '/admin/dashboard', '/admin/category'];
  if (!excludedPaths.includes(window.location.pathname)) {
    return <Navigation />;
  }
}

const AppRouter = () => {
 const existToken = localStorage.getItem('ACCESS_TOKEN');
 return (
  <>
    { existToken ? 
      (
       <Router>
        <Switch>
          {/* 所有管理员路由用AdminDefaultLayout包裹 */}
          <Route path="/admin/dashboard" render={(props) => (
            <AdminDefaultLayout>
              <Dashboard {...props} />
            </AdminDefaultLayout>
          )}/>
          <Route path="/admin/category" render={(props) => (
            <AdminDefaultLayout>
              <Category {...props} />
            </AdminDefaultLayout>
          )}/>
        </Switch>
       </Router>
      ) : (
       <Router>
        {renderNavigation()}
        <Switch>
         <Route path="/login" exact component={Login} />
         <Route path="/" exact component={HomeFour} />
        </Switch>
       </Router>
       ) 
    }
   </>
  )
}
export default AppRouter;

方法二:使用react-router-dom v5的嵌套路由

通过父路由匹配/admin前缀,在布局内部渲染子路由:

1. 修改AdminDefaultLayout.jsx

在布局内部添加Switch和子路由:

import React from 'react';
import { Route, Switch } from 'react-router-dom';
import Header from './Header';
import Sidebar from './Sidebar';
import Footer from './Footer';
import Dashboard from './pages/admin/Dashboard';
import Category from './pages/admin/Category';

const AdminDefaultLayout = () => {
  return (
    <>
      <div className="container">
        <Header />
        <Sidebar />
        <main>
          <Switch>
            {/* 子路由path无需完整路径,父路由已匹配/admin前缀 */}
            <Route path="/admin/dashboard" component={Dashboard} />
            <Route path="/admin/category" component={Category} />
          </Switch>
        </main>
        <Footer />
      </div>
    </>
  );
};

export default AdminDefaultLayout;

2. 调整路由写法

父路由匹配所有/admin开头的路径,渲染布局组件:

import React from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import HomeFour from "./components/pages/HomeFour";
import Login from "./components/pages/Login";
import AdminDefaultLayout from "./components/AdminDefaultLayout";
import Navigation from "./components/Navigation";

const renderNavigation = () => {
  const excludedPaths = ['/login', '/signup', '/coming-soon', '/error-404', '/admin/dashboard', '/admin/category'];
  if (!excludedPaths.includes(window.location.pathname)) {
    return <Navigation />;
  }
}

const AppRouter = () => {
 const existToken = localStorage.getItem('ACCESS_TOKEN');
 return (
  <>
    { existToken ? 
      (
       <Router>
        <Switch>
          {/* 父路由匹配所有/admin开头的路径 */}
          <Route path="/admin" component={AdminDefaultLayout} />
        </Switch>
       </Router>
      ) : (
       <Router>
        {renderNavigation()}
        <Switch>
         <Route path="/login" exact component={Login} />
         <Route path="/" exact component={HomeFour} />
        </Switch>
       </Router>
       ) 
    }
   </>
  )
}
export default AppRouter;

额外优化建议

当前renderNavigation直接操作window.location.pathname,在React中建议用useLocation钩子获取路径,避免直接操作DOM:

import { useLocation } from 'react-router-dom';

const NavigationWrapper = () => {
  const location = useLocation();
  const excludedPaths = ['/login', '/signup', '/coming-soon', '/error-404', '/admin/dashboard', '/admin/category'];
  if (!excludedPaths.includes(location.pathname)) {
    return <Navigation />;
  }
  return null;
}

然后在路由中替换{renderNavigation()}为<NavigationWrapper />,确保在Router内部使用钩子。

内容的提问来源于stack exchange,提问作者Muhammad Taha

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 18:54:55