Skip to content

Commit 8b4c234

Browse files
ztannerhuozhi
authored andcommitted
prevent erroneous route interception during lazy fetch (#64692)
When the router cache can't find a cache node for the requested segment, it performs a request to the server to get the missing data. This request to the server currently will always include the `next-url` header, and on soft-navigations, the router will route the request to the intercepted handler. This lazy fetch is treated as a soft navigation by the server, and will incorrectly return data for the intercepted route. Similar to the handling in `router.refresh`, and the server action reducer, we should not include the `next-url` header if there's no interception route currently in the tree, as otherwise we'll be erroneously triggering the intercepted route. Fixes #64676 Closes NEXT-3146
1 parent d6a7ca0 commit 8b4c234

File tree

2 files changed

+36
-1
lines changed

2 files changed

+36
-1
lines changed

packages/next/src/client/components/layout-router.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import { RedirectBoundary } from './redirect-boundary'
3434
import { NotFoundBoundary } from './not-found-boundary'
3535
import { getSegmentValue } from './router-reducer/reducers/get-segment-value'
3636
import { createRouterCacheKey } from './router-reducer/create-router-cache-key'
37+
import { hasInterceptionRouteInCurrentTree } from './router-reducer/reducers/has-interception-route-in-current-tree'
3738

3839
/**
3940
* Add refetch marker to router state at the point of the current layout segment.
@@ -408,10 +409,11 @@ function InnerLayoutRouter({
408409
*/
409410
// TODO-APP: remove ''
410411
const refetchTree = walkAddRefetch(['', ...segmentPath], fullTree)
412+
const includeNextUrl = hasInterceptionRouteInCurrentTree(fullTree)
411413
childNode.lazyData = lazyData = fetchServerResponse(
412414
new URL(url, location.origin),
413415
refetchTree,
414-
context.nextUrl,
416+
includeNextUrl ? context.nextUrl : null,
415417
buildId
416418
)
417419
childNode.lazyDataResolved = false

test/e2e/app-dir/parallel-routes-revalidation/parallel-routes-revalidation.test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,39 @@ createNextDescribe(
158158
})
159159
})
160160

161+
it('should not trigger the intercepted route when lazy-fetching missing data', async () => {
162+
const browser = await next.browser('/')
163+
164+
// trigger the interception page
165+
await browser.elementByCss("[href='/detail-page']").click()
166+
167+
// we should see the intercepted page
168+
expect(await browser.elementById('detail-title').text()).toBe(
169+
'Detail Page (Intercepted)'
170+
)
171+
172+
// refresh the page
173+
await browser.refresh()
174+
175+
// we should see the detail page
176+
expect(await browser.elementById('detail-title').text()).toBe(
177+
'Detail Page (Non-Intercepted)'
178+
)
179+
180+
// go back to the previous page
181+
await browser.back()
182+
183+
// reload the page, which will cause the router to no longer have cache nodes
184+
await browser.refresh()
185+
186+
// go forward, this will trigger a lazy fetch for the missing data, and should restore the detail page
187+
await browser.forward()
188+
189+
expect(await browser.elementById('detail-title').text()).toBe(
190+
'Detail Page (Non-Intercepted)'
191+
)
192+
})
193+
161194
describe.each([
162195
{ basePath: '/refreshing', label: 'regular', withSearchParams: false },
163196
{ basePath: '/refreshing', label: 'regular', withSearchParams: true },

0 commit comments

Comments
 (0)