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

Next.js App Router:如何将page.tsx的props传递给Layout.tsx?

实现路由角色对应的动态侧边栏方案

针对你遇到的「各角色页面定义的sidebarItems无法传递给DashboardLayout」的问题,以下是三种可行的实现方案,适配Next.js App Router(结合你提到的page.tsx结构):

方案一:React Context 传递侧边栏配置

通过创建全局Context,让每个角色页面提供对应的侧边栏数据,再在Layout中消费该数据,适合侧边栏与页面强关联的场景。

步骤1:创建侧边栏Context

// app/dashboard/context/SidebarContext.tsx
'use client';

import { createContext, useContext, ReactNode } from 'react';

// 定义侧边栏项类型
export type SidebarItem = {
  label: string;
  href: string;
  icon?: ReactNode;
};

type SidebarContextType = {
  sidebarItems: SidebarItem[];
};

const SidebarContext = createContext<SidebarContextType | undefined>(undefined);

export function SidebarProvider({ 
  children, 
  sidebarItems 
}: { 
  children: ReactNode; 
  sidebarItems: SidebarItem[] 
}) {
  return (
    <SidebarContext.Provider value={{ sidebarItems }}>
      {children}
    </SidebarContext.Provider>
  );
}

export function useSidebar() {
  const context = useContext(SidebarContext);
  if (!context) {
    throw new Error('useSidebar 必须在 SidebarProvider 内部使用');
  }
  return context;
}

步骤2:在角色页面中提供配置

// app/dashboard/admin/page.tsx
import { SidebarProvider, SidebarItem } from '../context/SidebarContext';

// Admin 角色的侧边栏配置
const adminSidebarItems: SidebarItem[] = [
  { label: '用户管理', href: '/dashboard/admin/users' },
  { label: '角色配置', href: '/dashboard/admin/roles' },
  { label: '系统日志', href: '/dashboard/admin/logs' },
];

export default function AdminPage() {
  return (
    <SidebarProvider sidebarItems={adminSidebarItems}>
      <h1>管理员仪表盘</h1>
      {/* 页面其他内容 */}
    </SidebarProvider>
  );
}

步骤3:在DashboardLayout中消费数据

// app/dashboard/layout.tsx
'use client';

import { useSidebar } from './context/SidebarContext';

export default function DashboardLayout({ children }: { children: ReactNode }) {
  const { sidebarItems } = useSidebar();

  return (
    <div className="flex h-screen">
      {/* 动态侧边栏 */}
      <aside className="w-64 bg-gray-100 border-r">
        <nav className="p-4">
          {sidebarItems.map((item, idx) => (
            <a
              key={idx}
              href={item.href}
              className="block py-2 px-3 rounded hover:bg-gray-200 mb-1"
            >
              {item.label}
            </a>
          ))}
        </nav>
      </aside>
      {/* 主内容区 */}
      <main className="flex-1 p-6 overflow-auto">
        {children}
      </main>
    </div>
  );
}

方案二:利用路由元数据(Next.js App Router)

Next.js App Router允许在page.tsx中自定义扩展元数据,通过路由路径匹配对应的元数据,无需额外Context。

步骤1:在角色页面定义元数据

// app/dashboard/company/page.tsx
import type { SidebarItem } from '../types';

// 自定义元数据,包含侧边栏配置
export const metadata = {
  sidebarItems: [
    { label: '企业信息', href: '/dashboard/company/profile' },
    { label: '职位发布', href: '/dashboard/company/jobs' },
    { label: '简历管理', href: '/dashboard/company/resumes' },
  ] as SidebarItem[],
};

export default function CompanyPage() {
  return <h1>企业仪表盘</h1>;
}

步骤2:在Layout中匹配路由元数据

// app/dashboard/layout.tsx
'use client';

import { usePathname } from 'next/navigation';
import { metadata as adminMeta } from './admin/page';
import { metadata as companyMeta } from './company/page';
import { metadata as studentMeta } from './student/page';
import type { SidebarItem } from './types';

// 建立路由与元数据的映射
const routeMetaMap: Record<string, { sidebarItems: SidebarItem[] }> = {
  '/dashboard/admin': adminMeta,
  '/dashboard/company': companyMeta,
  '/dashboard/student': studentMeta,
};

export default function DashboardLayout({ children }: { children: ReactNode }) {
  const pathname = usePathname();
  
  // 匹配当前路由对应的元数据(支持子路由继承父路由配置)
  const matchedRoute = Object.keys(routeMetaMap).find(route => 
    pathname.startsWith(route)
  ) || '/dashboard/admin';
  const { sidebarItems } = routeMetaMap[matchedRoute];

  return (
    <div className="flex h-screen">
      <aside className="w-64 bg-gray-100 border-r">
        <nav className="p-4">
          {sidebarItems.map((item, idx) => (
            <a
              key={idx}
              href={item.href}
              className="block py-2 px-3 rounded hover:bg-gray-200 mb-1"
            >
              {item.label}
            </a>
          ))}
        </nav>
      </aside>
      <main className="flex-1 p-6 overflow-auto">
        {children}
      </main>
    </div>
  );
}

方案三:基于RBAC角色直接生成侧边栏

如果侧边栏完全由用户角色决定,无需和页面绑定,可以直接在Layout中根据当前用户角色读取预设的侧边栏配置,逻辑更简洁。

// app/dashboard/layout.tsx
'use client';

import { useUser } from './hooks/useUser'; // 自定义hook,获取当前用户角色
import type { SidebarItem } from './types';

// 按角色预设侧边栏配置
const roleSidebarConfig: Record<string, SidebarItem[]> = {
  admin: [
    { label: '用户管理', href: '/dashboard/admin/users' },
    { label: '角色配置', href: '/dashboard/admin/roles' },
  ],
  company: [
    { label: '企业信息', href: '/dashboard/company/profile' },
    { label: '职位管理', href: '/dashboard/company/jobs' },
  ],
  student: [
    { label: '我的简历', href: '/dashboard/student/resume' },
    { label: '申请记录', href: '/dashboard/student/applications' },
  ],
};

export default function DashboardLayout({ children }: { children: ReactNode }) {
  const { user } = useUser();
  // 根据用户角色获取对应侧边栏,默认空数组
  const sidebarItems = roleSidebarConfig[user?.role || ''] || [];

  return (
    <div className="flex h-screen">
      <aside className="w-64 bg-gray-100 border-r">
        <nav className="p-4">
          {sidebarItems.map((item, idx) => (
            <a
              key={idx}
              href={item.href}
              className="block py-2 px-3 rounded hover:bg-gray-200 mb-1"
            >
              {item.label}
            </a>
          ))}
        </nav>
      </aside>
      <main className="flex-1 p-6 overflow-auto">
        {children}
      </main>
    </div>
  );
}

方案选型建议

  • Context方案:适合同角色不同页面需要展示不同侧边栏的场景,灵活度最高。
  • 路由元数据方案:适合Next.js环境,配置与页面耦合度低,无需额外状态管理。
  • RBAC角色映射方案:适合侧边栏完全由角色决定的场景,代码最简洁,维护成本低。

内容的提问来源于stack exchange,提问作者Anish Shobith P S

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 03:17:15