如何从嵌套路由向页面布局的其他区域传递元素?
实现带动态侧边栏/页眉的嵌套路由布局方案
核心思路
React Router 的 Route 组件不支持自定义属性(比如你伪代码里的sidebar/header),我们需要通过上下文(Context)+ 路由包装组件的方式,把父路由定义的侧边栏、页眉传递给顶层布局组件,同时保持布局和内容的解耦。
步骤1:创建布局上下文
定义一个上下文,用来存储当前路由对应的侧边栏和页眉组件:
// src/contexts/LayoutContext.tsx import { createContext, useContext, ReactNode } from 'react'; type LayoutContextType = { sidebar: ReactNode | null; header: ReactNode | null; }; const LayoutContext = createContext<LayoutContextType>({ sidebar: null, header: null, }); export const LayoutProvider = LayoutContext.Provider; export const useLayout = () => useContext(LayoutContext);
步骤2:改造布局组件
修改DefaultLayout,通过上下文获取侧边栏和页眉并渲染:
// src/layouts/DefaultLayout.tsx import { Outlet } from 'react-router-dom'; import { useLayout } from '../contexts/LayoutContext'; export function DefaultLayout() { const { sidebar, header } = useLayout(); return ( <div style={{ display: 'flex', height: '100vh' }}> {/* 渲染侧边栏 */} {sidebar && <div className="sidebar">{sidebar}</div>} <div className="page" style={{ flex: 1, display: 'flex', flexDirection: 'column' }}> {/* 渲染页眉 */} {header && <div className="page-header">{header}</div>} {/* 渲染子路由内容 */} <div className="page-body" style={{ flex: 1, overflow: 'auto' }}> <Outlet /> </div> </div> </div> ); }
步骤3:创建路由包装组件
写一个自定义路由包装组件,用来接收sidebar/header属性,并通过上下文传递给布局:
// src/components/RouteWithLayout.tsx import { Route, RouteProps } from 'react-router-dom'; import { LayoutProvider } from '../contexts/LayoutContext'; import { ReactNode } from 'react'; type RouteWithLayoutProps = RouteProps & { sidebar?: ReactNode; header?: ReactNode; }; export function RouteWithLayout({ sidebar, header, ...props }: RouteWithLayoutProps) { return ( <LayoutProvider value={{ sidebar: sidebar || null, header: header || null }}> <Route {...props} /> </LayoutProvider> ); }
步骤4:配置路由
用RouteWithLayout代替原生Route,在父路由里定义对应的侧边栏和页眉:
// src/routes/AppRoutes.tsx import { Routes } from 'react-router-dom'; import { DefaultLayout } from '../layouts/DefaultLayout'; import { RouteWithLayout } from '../components/RouteWithLayout'; // 导入你的页面组件 import DefaultSidebar from '../components/DefaultSidebar'; import DashboardHeader from '../components/DashboardHeader'; import DashboardHomePage from '../pages/DashboardHomePage'; import DashboardStatsPage from '../pages/DashboardStatsPage'; import DashboardHelpPage from '../pages/DashboardHelpPage'; import UserSidebar from '../components/UserSidebar'; import UserHeader from '../components/UserHeader'; import UserOverviewPage from '../pages/UserOverviewPage'; import UserAccountsPage from '../pages/UserAccountsPage'; export function AppRoutes() { return ( <Routes> <Route element={<DefaultLayout />}> {/* Dashboard 分组路由,带专属侧边栏和页眉 */} <RouteWithLayout path="/dashboard" sidebar={<DefaultSidebar />} header={<DashboardHeader />} > <Route index element={<DashboardHomePage />} /> <Route path="stats" element={<DashboardStatsPage />} /> <Route path="help" element={<DashboardHelpPage />} /> </RouteWithLayout> {/* User 分组路由,带专属侧边栏和页眉 */} <RouteWithLayout path="/user" sidebar={<UserSidebar />} header={<UserHeader />} > <Route index element={<UserOverviewPage />} /> <Route path="accounts" element={<UserAccountsPage />} /> </RouteWithLayout> </Route> </Routes> ); }
避免无限循环的注意事项
- 不要在路由配置里动态创建组件实例(比如不要写
sidebar={() => <DefaultSidebar />}),直接传递组件实例即可。 - 如果侧边栏/页眉是复杂组件,建议用
React.memo包裹,避免不必要的重渲染。
内容的提问来源于stack exchange,提问作者Katai
相关产品推荐
相关产品推荐

