Skip to content

Commit

Permalink
prevent erroneous route interception during lazy fetch (#64692)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
ztanner authored and huozhi committed Apr 23, 2024
1 parent d6a7ca0 commit 8b4c234
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 1 deletion.
4 changes: 3 additions & 1 deletion packages/next/src/client/components/layout-router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { RedirectBoundary } from './redirect-boundary'
import { NotFoundBoundary } from './not-found-boundary'
import { getSegmentValue } from './router-reducer/reducers/get-segment-value'
import { createRouterCacheKey } from './router-reducer/create-router-cache-key'
import { hasInterceptionRouteInCurrentTree } from './router-reducer/reducers/has-interception-route-in-current-tree'

/**
* Add refetch marker to router state at the point of the current layout segment.
Expand Down Expand Up @@ -408,10 +409,11 @@ function InnerLayoutRouter({
*/
// TODO-APP: remove ''
const refetchTree = walkAddRefetch(['', ...segmentPath], fullTree)
const includeNextUrl = hasInterceptionRouteInCurrentTree(fullTree)
childNode.lazyData = lazyData = fetchServerResponse(
new URL(url, location.origin),
refetchTree,
context.nextUrl,
includeNextUrl ? context.nextUrl : null,
buildId
)
childNode.lazyDataResolved = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,39 @@ createNextDescribe(
})
})

it('should not trigger the intercepted route when lazy-fetching missing data', async () => {
const browser = await next.browser('/')

// trigger the interception page
await browser.elementByCss("[href='/detail-page']").click()

// we should see the intercepted page
expect(await browser.elementById('detail-title').text()).toBe(
'Detail Page (Intercepted)'
)

// refresh the page
await browser.refresh()

// we should see the detail page
expect(await browser.elementById('detail-title').text()).toBe(
'Detail Page (Non-Intercepted)'
)

// go back to the previous page
await browser.back()

// reload the page, which will cause the router to no longer have cache nodes
await browser.refresh()

// go forward, this will trigger a lazy fetch for the missing data, and should restore the detail page
await browser.forward()

expect(await browser.elementById('detail-title').text()).toBe(
'Detail Page (Non-Intercepted)'
)
})

describe.each([
{ basePath: '/refreshing', label: 'regular', withSearchParams: false },
{ basePath: '/refreshing', label: 'regular', withSearchParams: true },
Expand Down

0 comments on commit 8b4c234

Please sign in to comment.