Skip to content

Commit b3203e9

Browse files
fix(next/client): fall back to full-page navigation on segment cache fetch failure
Fixes #97807
1 parent fa1eab6 commit b3203e9

2 files changed

Lines changed: 67 additions & 3 deletions

File tree

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/**
2+
* @jest-environment jsdom
3+
*/
4+
5+
import { TextEncoder, TextDecoder } from 'util'
6+
global.TextEncoder = TextEncoder
7+
global.TextDecoder = TextDecoder as any
8+
;(globalThis as any).__webpack_require__ = () => {}
9+
;(globalThis as any).__webpack_chunk_load__ = () => Promise.resolve()
10+
11+
const { completeHardNavigation } =
12+
require('./app-router-state') as typeof import('./app-router-state')
13+
import type { AppRouterState } from './router-reducer/router-reducer-types'
14+
15+
describe('app-router-state completeHardNavigation', () => {
16+
it('creates MPA fallback navigation state for failed route requests', () => {
17+
const previousState: AppRouterState = {
18+
buildId: 'test-build',
19+
tree: ['/', {}],
20+
cache: {
21+
lazyData: null,
22+
rsc: null,
23+
prefetchRsc: null,
24+
head: null,
25+
prefetchHead: null,
26+
parallelRoutes: new Map(),
27+
loading: null,
28+
},
29+
prefetchCache: new Map(),
30+
pushRef: {
31+
pendingPush: false,
32+
mpaNavigation: false,
33+
preserveCustomHistoryState: false,
34+
},
35+
focusAndScrollRef: {
36+
apply: false,
37+
onlyHashChange: false,
38+
hashFragment: null,
39+
segmentPaths: [],
40+
},
41+
canonicalUrl: '/current-page?tab=old',
42+
renderedSearch: '?tab=old',
43+
nextUrl: null,
44+
}
45+
46+
const targetUrl = new URL('/destination', location.origin)
47+
const nextState = completeHardNavigation(previousState, targetUrl, 'push')
48+
49+
expect(nextState.canonicalUrl).toBe('/destination')
50+
expect(nextState.pushRef.mpaNavigation).toBe(true)
51+
expect(nextState.pushRef.pendingPush).toBe(true)
52+
})
53+
})

‎packages/next/src/client/components/app-router-state.ts‎

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -217,9 +217,20 @@ function navigateImpl(
217217
navigateType,
218218
navigationLock,
219219
map
220-
).catch(() => {
221-
// If the navigation fails, return the current state
222-
return state
220+
).catch((error) => {
221+
// If the navigation was intentionally aborted (e.g. superseded by another navigation), return the current state
222+
if (
223+
error &&
224+
(error.name === 'AbortError' ||
225+
error.name === 'TimeoutError' ||
226+
(typeof DOMException !== 'undefined' &&
227+
error instanceof DOMException &&
228+
error.name === 'AbortError'))
229+
) {
230+
return state
231+
}
232+
// If the navigation fails because of a network error or rejected server response, fall back to a full-page (MPA) navigation
233+
return completeHardNavigation(state, url, navigateType)
223234
})
224235
}
225236

0 commit comments

Comments
 (0)