diff --git a/packages/next/src/client/components/router-reducer/refetch-inactive-parallel-segments.ts b/packages/next/src/client/components/router-reducer/refetch-inactive-parallel-segments.ts index 0da1da7e9e6027..192ec4eb215cba 100644 --- a/packages/next/src/client/components/router-reducer/refetch-inactive-parallel-segments.ts +++ b/packages/next/src/client/components/router-reducer/refetch-inactive-parallel-segments.ts @@ -27,7 +27,11 @@ export async function refreshInactiveParallelSegments( options: RefreshInactiveParallelSegments ) { const fetchedSegments = new Set() - await refreshInactiveParallelSegmentsImpl({ ...options, fetchedSegments }) + await refreshInactiveParallelSegmentsImpl({ + ...options, + rootTree: options.updatedTree, + fetchedSegments, + }) } async function refreshInactiveParallelSegmentsImpl({ @@ -36,7 +40,11 @@ async function refreshInactiveParallelSegmentsImpl({ updatedCache, includeNextUrl, fetchedSegments, -}: RefreshInactiveParallelSegments & { fetchedSegments: Set }) { + rootTree = updatedTree, +}: RefreshInactiveParallelSegments & { + fetchedSegments: Set + rootTree: FlightRouterState +}) { const [, parallelRoutes, refetchPathname, refetchMarker] = updatedTree const fetchPromises = [] @@ -56,7 +64,9 @@ async function refreshInactiveParallelSegmentsImpl({ // we capture the pathname of the refetch without search params, so that it can be refetched with // the "latest" search params when it comes time to actually trigger the fetch (below) new URL(refetchPathname + location.search, location.origin), - [updatedTree[0], updatedTree[1], updatedTree[2], 'refetch'], + // refetch from the root of the updated tree, otherwise it will be scoped to the current segment + // and might not contain the data we need to patch in interception route data (such as dynamic params from a previous segment) + [rootTree[0], rootTree[1], rootTree[2], 'refetch'], includeNextUrl ? state.nextUrl : null, state.buildId ).then((fetchResponse) => { @@ -85,6 +95,7 @@ async function refreshInactiveParallelSegmentsImpl({ updatedCache, includeNextUrl, fetchedSegments, + rootTree, }) fetchPromises.push(parallelFetchPromise) @@ -104,7 +115,8 @@ export function addRefreshMarkerToActiveParallelSegments( pathname: string ) { const [segment, parallelRoutes, , refetchMarker] = tree - if (segment === PAGE_SEGMENT_KEY && refetchMarker !== 'refresh') { + // a page segment might also contain concatenated search params, so we do a partial match on the key + if (segment.includes(PAGE_SEGMENT_KEY) && refetchMarker !== 'refresh') { tree[2] = pathname tree[3] = 'refresh' } diff --git a/test/e2e/app-dir/parallel-routes-revalidation/app/refreshing/buttonRefresh.tsx b/test/e2e/app-dir/parallel-routes-revalidation/app/components/RefreshButton.tsx similarity index 88% rename from test/e2e/app-dir/parallel-routes-revalidation/app/refreshing/buttonRefresh.tsx rename to test/e2e/app-dir/parallel-routes-revalidation/app/components/RefreshButton.tsx index a839189509d1b1..0afed1130e88df 100644 --- a/test/e2e/app-dir/parallel-routes-revalidation/app/refreshing/buttonRefresh.tsx +++ b/test/e2e/app-dir/parallel-routes-revalidation/app/components/RefreshButton.tsx @@ -1,7 +1,7 @@ 'use client' import { useRouter } from 'next/navigation' -export function Button() { +export function RefreshButton() { const router = useRouter() return ( diff --git a/test/e2e/app-dir/parallel-routes-revalidation/app/components/RevalidateButton.tsx b/test/e2e/app-dir/parallel-routes-revalidation/app/components/RevalidateButton.tsx new file mode 100644 index 00000000000000..8773173b63d123 --- /dev/null +++ b/test/e2e/app-dir/parallel-routes-revalidation/app/components/RevalidateButton.tsx @@ -0,0 +1,14 @@ +'use client' +import { revalidateAction } from '../nested-revalidate/@modal/modal/action' + +export function RevalidateButton({ id }: { id?: string }) { + return ( + + ) +} diff --git a/test/e2e/app-dir/parallel-routes-revalidation/app/dynamic-refresh/[dynamic]/@modal/(.)login/page.tsx b/test/e2e/app-dir/parallel-routes-revalidation/app/dynamic-refresh/[dynamic]/@modal/(.)login/page.tsx new file mode 100644 index 00000000000000..42f51e7bd7c7aa --- /dev/null +++ b/test/e2e/app-dir/parallel-routes-revalidation/app/dynamic-refresh/[dynamic]/@modal/(.)login/page.tsx @@ -0,0 +1,22 @@ +import { RefreshButton } from '../../../../components/RefreshButton' +import { RevalidateButton } from '../../../../components/RevalidateButton' + +const getRandom = async () => Math.random() + +export default async function Page({ params }) { + const someProp = await getRandom() + + return ( + +
{params.dynamic}
+
+
+ Modal Page + {someProp} +
+ + +
+
+ ) +} diff --git a/test/e2e/app-dir/parallel-routes-revalidation/app/dynamic-refresh/[dynamic]/@modal/default.tsx b/test/e2e/app-dir/parallel-routes-revalidation/app/dynamic-refresh/[dynamic]/@modal/default.tsx new file mode 100644 index 00000000000000..86b9e9a3881296 --- /dev/null +++ b/test/e2e/app-dir/parallel-routes-revalidation/app/dynamic-refresh/[dynamic]/@modal/default.tsx @@ -0,0 +1,3 @@ +export default function Default() { + return null +} diff --git a/test/e2e/app-dir/parallel-routes-revalidation/app/dynamic-refresh/[dynamic]/layout.tsx b/test/e2e/app-dir/parallel-routes-revalidation/app/dynamic-refresh/[dynamic]/layout.tsx new file mode 100644 index 00000000000000..87f82a618a2290 --- /dev/null +++ b/test/e2e/app-dir/parallel-routes-revalidation/app/dynamic-refresh/[dynamic]/layout.tsx @@ -0,0 +1,19 @@ +import Link from 'next/link' + +export const dynamic = 'force-dynamic' + +export default function Layout({ + children, + modal, +}: { + children: React.ReactNode + modal: React.ReactNode +}) { + return ( +
+
{children}
+
{modal}
+ Go to Other Page +
+ ) +} diff --git a/test/e2e/app-dir/parallel-routes-revalidation/app/dynamic-refresh/[dynamic]/login/page.tsx b/test/e2e/app-dir/parallel-routes-revalidation/app/dynamic-refresh/[dynamic]/login/page.tsx new file mode 100644 index 00000000000000..fe7a2d2912562b --- /dev/null +++ b/test/e2e/app-dir/parallel-routes-revalidation/app/dynamic-refresh/[dynamic]/login/page.tsx @@ -0,0 +1,11 @@ +import { RefreshButton } from '../../../components/RefreshButton' + +export default function Page() { + return ( + <> + Login Page + + Random Number: {Math.random()} + + ) +} diff --git a/test/e2e/app-dir/parallel-routes-revalidation/app/dynamic-refresh/[dynamic]/other/page.tsx b/test/e2e/app-dir/parallel-routes-revalidation/app/dynamic-refresh/[dynamic]/other/page.tsx new file mode 100644 index 00000000000000..f7d6f1f5a51437 --- /dev/null +++ b/test/e2e/app-dir/parallel-routes-revalidation/app/dynamic-refresh/[dynamic]/other/page.tsx @@ -0,0 +1,13 @@ +import { RefreshButton } from '../../../components/RefreshButton' +import { RevalidateButton } from '../../../components/RevalidateButton' + +export default function Page() { + return ( +
+
Other Page
+
{Math.random()}
+ + +
+ ) +} diff --git a/test/e2e/app-dir/parallel-routes-revalidation/app/dynamic-refresh/[dynamic]/page.tsx b/test/e2e/app-dir/parallel-routes-revalidation/app/dynamic-refresh/[dynamic]/page.tsx new file mode 100644 index 00000000000000..2634584a60bbe9 --- /dev/null +++ b/test/e2e/app-dir/parallel-routes-revalidation/app/dynamic-refresh/[dynamic]/page.tsx @@ -0,0 +1,14 @@ +import Link from 'next/link' + +export default function Home() { + return ( +
+ + + +
+ Random # from Root Page: {Math.random()} +
+
+ ) +} diff --git a/test/e2e/app-dir/parallel-routes-revalidation/app/refreshing/@modal/(.)login/page.tsx b/test/e2e/app-dir/parallel-routes-revalidation/app/refreshing/@modal/(.)login/page.tsx index cdab476c92b926..30496df0e5a610 100644 --- a/test/e2e/app-dir/parallel-routes-revalidation/app/refreshing/@modal/(.)login/page.tsx +++ b/test/e2e/app-dir/parallel-routes-revalidation/app/refreshing/@modal/(.)login/page.tsx @@ -1,4 +1,5 @@ -import { Button } from '../../buttonRefresh' +import { RefreshButton } from '../../../components/RefreshButton' +import { RevalidateButton } from '../../../components/RevalidateButton' const getRandom = async () => Math.random() @@ -12,7 +13,8 @@ export default async function Page() { Modal Page {someProp} -