Next.js 13(App Router)动态路由向Layout传递Props方案
解决Next.js动态路由独立布局并传递Props的问题
方案一:通过路由分组隔离独立布局
Next.js的路由分组(括号包裹的目录)可让不同路由组使用各自的根布局,避免嵌套。调整项目结构如下:
/app -/(site) --/layout.tsx # 原站点布局,负责首页等路由 --/page.tsx -/(dynamic-slug) # 新增独立路由组,承载动态[slug]路由 --/[slug]/page.tsx --/[slug]/layout.tsx # 动态路由专属独立布局
此时/(dynamic-slug)/[slug]/layout.tsx会成为该路由的根布局,不再嵌套/(site)/layout.tsx。接下来在这个独立布局中处理Props传递:
动态路由布局中获取并传递Props
// /app/(dynamic-slug)/[slug]/layout.tsx import { Suspense } from 'react'; import { getSlugSettings } from '@/lib/data'; // 自定义数据获取函数 import SlugMasthead from '@/components/SlugMasthead'; import Foundation from '@/components/Foundation'; export default async function SlugLayout({ children, params, }: { children: React.ReactNode; params: { slug: string }; }) { // 根据slug获取对应数据作为Props const slugSettings = await getSlugSettings(params.slug); return ( <html lang='en'> <body> <div className='flex min-h-screen flex-col'> <a className='sr-only' href='#mainContent'> Skip to content </a> <Suspense> {/* 向组件传递Props */} <SlugMasthead pageTitle={slugSettings.pageTitle} /> </Suspense> <main className='flex flex-1 flex-col' id='mainContent'> {/* 若需子页面访问Props,用React Context传递 */} <SlugContext.Provider value={slugSettings}> <Suspense>{children}</Suspense> </SlugContext.Provider> </main> <Suspense> <Foundation menus={slugSettings.footerMenus} /> </Suspense> </div> </body> </html> ); }
用React Context让子页面接收Props
创建Context文件:
// /app/context/slug.tsx import { createContext, useContext } from 'react'; type SlugSettings = { pageTitle: string; footerMenus: MenuItem[]; // 其他需传递的字段 }; const SlugContext = createContext<SlugSettings | undefined>(undefined); export function SlugProvider({ children, value, }: { children: React.ReactNode; value: SlugSettings; }) { return <SlugContext.Provider value={value}>{children}</SlugContext.Provider>; } export function useSlugContext() { const context = useContext(SlugContext); if (!context) { throw new Error('useSlugContext must be used within a SlugProvider'); } return context; }
之后在子页面中通过useSlugContext()即可获取布局传递的Props。
方案二:在现有布局中按路由分支处理(不调整路由结构)
若不想修改路由结构,可在/(site)/layout.tsx中根据当前路由判断,渲染不同布局内容并传递对应Props:
// /app/(site)/layout.tsx import { Suspense } from 'react'; import { usePathname } from 'next/navigation'; import Masthead from '@/components/Masthead'; import SlugMasthead from '@/components/SlugMasthead'; import Foundation from '@/components/Foundation'; import { getSettings, getSlugSettings } from '@/lib/data'; export default async function SiteLayout({ children, params, }: { children: React.ReactNode; params?: { slug: string }; }) { const pathname = usePathname(); const isDynamicSlug = params?.slug && pathname === `/${params.slug}`; // 根据路由获取对应数据 const baseSettings = await getSettings(); const slugSettings = isDynamicSlug ? await getSlugSettings(params.slug) : null; return ( <html lang='en'> <body> <div className='flex min-h-screen flex-col'> <a className='sr-only' href='#mainContent'> Skip to content </a> <Suspense> {/* 按路由渲染不同组件并传递Props */} {isDynamicSlug && slugSettings ? ( <SlugMasthead pageTitle={slugSettings.pageTitle} /> ) : ( <Masthead /> )} </Suspense> <main className='flex flex-1 flex-col' id='mainContent'> <Suspense>{children}</Suspense> </main> <Suspense> <Foundation menus={slugSettings?.footerMenus || baseSettings.menus.footer} /> </Suspense> </div> </body> </html> ); }
这种方式无需调整路由,但会增加主布局的逻辑复杂度,适合简单场景。
内容的提问来源于stack exchange,提问作者jacob
相关产品推荐
相关产品推荐

