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

Next.js App Router嵌套布局中动态获取页面UID实现数据过滤

解决Next.js App Router中动态UID过滤Context数据的问题

问题背景

我在Next.js App Router中用catchall路由构建了如下嵌套动态页面结构:

app/stay/
    |__[...category]
    |    |__page.js
    |__[uid]
    |   |__page.js
    |__layout.js

在layout.js中配置了Context Provider,用来存储过滤后的数据并包裹子页面:

export default async function StayLayout({ children }) {
  
  const client = createClient();
  const stay_pages = await client.getAllByType('stay_page',
    {
      fetchLinks: ["location_category.category_title", "stay_category.category_title"],
      orderings: [
        {
          field: "my.stay_page.listing_title",
          direction: "asc",
        },
      ],
    },
  )

  const filteredStayListings = Array.from(
    new Set(stay_pages.filter((item) => item.data.stay_category.uid === 'guest-ranches-lodges'))
  )

  return (
    <StayProvider 
      filteredStayListings={filteredStayListings}
    >
      {children}
    </StayProvider>
  )
}

当前生成的路径类似/stay/guest-ranches-lodges/lodge-name,需要把过滤条件里硬编码的'guest-ranches-lodges'替换为页面的动态UID,但父布局无法获取子页面的UID或路径参数,且Next.js文档明确:

无法在父布局与子页面之间传递数据。不过,你可以在路由中多次获取同一数据,React会自动去重,不会影响性能。

可行解决方案

方案1:将Context Provider下移到子页面

既然父布局拿不到子页面的参数,直接把Provider移到[...category]/page.js和[uid]/page.js中,在子页面里获取动态参数后过滤数据,再传递给Provider。

以[...category]/page.js为例:

import { StayProvider } from '@/components/StayProvider';
import createClient from '@/lib/client';

export default async function CategoryPage({ params }) {
  // 从catchall参数中提取目标UID,比如params.category[0]就是guest-ranches-lodges
  const targetUid = params.category[0];

  // 复用原布局中的数据获取逻辑
  const client = createClient();
  const stay_pages = await client.getAllByType('stay_page',
    {
      fetchLinks: ["location_category.category_title", "stay_category.category_title"],
      orderings: [
        {
          field: "my.stay_page.listing_title",
          direction: "asc",
        },
      ],
    },
  );

  // 根据动态UID过滤数据
  const filteredStayListings = stay_pages.filter(item => 
    item.data.stay_category.uid === targetUid
  );

  return (
    <StayProvider filteredStayListings={filteredStayListings}>
      {/* 页面内容 */}
      <div>...</div>
    </StayProvider>
  );
}

[uid]/page.js同理,直接从params.uid拿到目标值后过滤数据即可。

方案2:用数据缓存库实现复用与过滤

如果多个页面需要复用这份数据,可以用React Query或SWR做缓存,避免重复请求的同时,在需要的地方根据参数过滤:

  1. 先在工具层封装数据获取函数:
// lib/stays.js
import createClient from './client';

export async function getAllStayPages() {
  const client = createClient();
  return client.getAllByType('stay_page',
    {
      fetchLinks: ["location_category.category_title", "stay_category.category_title"],
      orderings: [
        {
          field: "my.stay_page.listing_title",
          direction: "asc",
        },
      ],
    },
  );
}
  1. 在子页面中用React Query获取并过滤:
'use client';
import { useQuery } from '@tanstack/react-query';
import { getAllStayPages } from '@/lib/stays';
import { StayProvider } from '@/components/StayProvider';

export default function CategoryPage({ params }) {
  const targetUid = params.category[0];
  const { data: stay_pages } = useQuery({
    queryKey: ['stayPages'],
    queryFn: getAllStayPages,
  });

  const filteredStayListings = stay_pages?.filter(item => 
    item.data.stay_category.uid === targetUid
  ) || [];

  return (
    <StayProvider filteredStayListings={filteredStayListings}>
      {/* 页面内容 */}
    </StayProvider>
  );
}

这种方式下,即使多个页面调用数据,缓存库会自动复用请求结果,符合Next.js文档中关于重复获取数据的性能提示。

方案3:调整路由结构让布局能拿到参数

如果允许调整路由,可以把动态参数提升到布局所在层级,比如:

app/stay/[categoryUid]/
    |__[...subpath]
    |    |__page.js
    |__layout.js

这样stay/[categoryUid]/layout.js就能直接拿到params.categoryUid,在布局里完成过滤并提供Context:

export default async function StayCategoryLayout({ children, params }) {
  const client = createClient();
  const stay_pages = await client.getAllByType('stay_page',
    {
      fetchLinks: ["location_category.category_title", "stay_category.category_title"],
      orderings: [
        {
          field: "my.stay_page.listing_title",
          direction: "asc",
        },
      ],
    },
  );

  const filteredStayListings = stay_pages.filter(item => 
    item.data.stay_category.uid === params.categoryUid
  );

  return (
    <StayProvider filteredStayListings={filteredStayListings}>
      {children}
    </StayProvider>
  );
}

总结

  • 方案1简单直接,无额外依赖,适合数据仅在当前子页面使用的场景;
  • 方案2适合多页面复用数据的场景,通过缓存减少请求开销;
  • 方案3需要调整路由结构,适合路由逻辑允许的情况。

内容的提问来源于stack exchange,提问作者J. Gunderson

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 20:25:55