diff --git a/packages/next/src/client/components/app-router.tsx b/packages/next/src/client/components/app-router.tsx index 2c302780295779..f99f449a32c449 100644 --- a/packages/next/src/client/components/app-router.tsx +++ b/packages/next/src/client/components/app-router.tsx @@ -162,7 +162,6 @@ function useServerActionDispatcher(dispatch: React.Dispatch) { dispatch({ ...actionPayload, type: ACTION_SERVER_ACTION, - mutable: {}, }) }) }, @@ -189,7 +188,6 @@ function useChangeByServerResponse( flightData, previousTree, overrideCanonicalUrl, - mutable: {}, }) }) }, @@ -209,7 +207,6 @@ function useNavigate(dispatch: React.Dispatch): RouterNavigate { locationSearch: location.search, shouldScroll: shouldScroll ?? true, navigateType, - mutable: {}, }) }, [dispatch] @@ -329,7 +326,6 @@ function Router({ startTransition(() => { dispatch({ type: ACTION_REFRESH, - mutable: {}, origin: window.location.origin, }) }) @@ -344,7 +340,6 @@ function Router({ startTransition(() => { dispatch({ type: ACTION_FAST_REFRESH, - mutable: {}, origin: window.location.origin, }) }) diff --git a/packages/next/src/client/components/router-reducer/reducers/fast-refresh-reducer.ts b/packages/next/src/client/components/router-reducer/reducers/fast-refresh-reducer.ts index fbe499d572e677..4fd9ef2c2b1ff3 100644 --- a/packages/next/src/client/components/router-reducer/reducers/fast-refresh-reducer.ts +++ b/packages/next/src/client/components/router-reducer/reducers/fast-refresh-reducer.ts @@ -6,6 +6,7 @@ import type { ReadonlyReducerState, ReducerState, FastRefreshAction, + Mutable, } from '../router-reducer-types' import { handleExternalUrl } from './navigate-reducer' import { handleMutable } from '../handle-mutable' @@ -18,16 +19,10 @@ function fastRefreshReducerImpl( state: ReadonlyReducerState, action: FastRefreshAction ): ReducerState { - const { mutable, origin } = action + const { origin } = action + const mutable: Mutable = {} const href = state.canonicalUrl - const isForCurrentTree = - JSON.stringify(mutable.previousTree) === JSON.stringify(state.tree) - - if (isForCurrentTree) { - return handleMutable(state, mutable) - } - mutable.preserveCustomHistoryState = false const cache: CacheNode = createEmptyCacheNode() diff --git a/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.test.tsx b/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.test.tsx index c757b59ebd935a..b76a2932f9b642 100644 --- a/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.test.tsx +++ b/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.test.tsx @@ -13,6 +13,7 @@ import { import type { NavigateAction, PrefetchAction } from '../router-reducer-types' import { navigateReducer } from './navigate-reducer' import { prefetchReducer } from './prefetch-reducer' +import { handleMutable } from '../handle-mutable' const buildId = 'development' @@ -106,19 +107,6 @@ const getInitialRouterStateTree = (): FlightRouterState => [ true, ] -async function runPromiseThrowChain(fn: any): Promise { - try { - return await fn() - } catch (err) { - if (err instanceof Promise) { - await err - return await runPromiseThrowChain(fn) - } - - throw err - } -} - describe('navigateReducer', () => { beforeAll(() => { jest.useFakeTimers() @@ -187,12 +175,9 @@ describe('navigateReducer', () => { locationSearch: '', navigateType: 'push', shouldScroll: true, - mutable: {}, } - const newState = await runPromiseThrowChain(() => - navigateReducer(state, action) - ) + const newState = await navigateReducer(state, action) expect(newState).toMatchInlineSnapshot(` { @@ -377,17 +362,6 @@ describe('navigateReducer', () => { location: new URL('/linking', 'https://localhost') as any, }) - const state2 = createInitialRouterState({ - buildId, - initialTree, - initialHead: null, - initialCanonicalUrl, - initialSeedData: ['', null, children], - initialParallelRoutes, - isServer: false, - location: new URL('/linking', 'https://localhost') as any, - }) - const action: NavigateAction = { type: ACTION_NAVIGATE, url: new URL('/linking/about', 'https://localhost'), @@ -395,14 +369,9 @@ describe('navigateReducer', () => { locationSearch: '', navigateType: 'push', shouldScroll: true, - mutable: {}, } - await runPromiseThrowChain(() => navigateReducer(state, action)) - - const newState = await runPromiseThrowChain(() => - navigateReducer(state2, action) - ) + const newState = await navigateReducer(state, action) expect(newState).toMatchInlineSnapshot(` { @@ -479,7 +448,31 @@ describe('navigateReducer', () => { ], }, "nextUrl": "/linking/about", - "prefetchCache": Map {}, + "prefetchCache": Map { + "/linking/about" => { + "data": Promise {}, + "kind": "temporary", + "lastUsedTime": 1690329600000, + "prefetchTime": 1690329600000, + "treeAtTimeOfPrefetch": [ + "", + { + "children": [ + "linking", + { + "children": [ + "__PAGE__", + {}, + ], + }, + ], + }, + undefined, + undefined, + true, + ], + }, + }, "pushRef": { "mpaNavigation": false, "pendingPush": true, @@ -563,17 +556,6 @@ describe('navigateReducer', () => { location: new URL('/linking', 'https://localhost') as any, }) - const state2 = createInitialRouterState({ - buildId, - initialTree, - initialHead: null, - initialCanonicalUrl, - initialSeedData: ['', null, children], - initialParallelRoutes, - isServer: false, - location: new URL('/linking', 'https://localhost') as any, - }) - const url = new URL('https://example.vercel.sh', 'https://localhost') const isExternalUrl = url.origin !== 'localhost' @@ -584,14 +566,9 @@ describe('navigateReducer', () => { locationSearch: '', navigateType: 'push', shouldScroll: true, - mutable: {}, } - await runPromiseThrowChain(() => navigateReducer(state, action)) - - const newState = await runPromiseThrowChain(() => - navigateReducer(state2, action) - ) + const newState = await navigateReducer(state, action) expect(newState).toMatchInlineSnapshot(` { @@ -716,17 +693,6 @@ describe('navigateReducer', () => { location: new URL('/linking', 'https://localhost') as any, }) - const state2 = createInitialRouterState({ - buildId, - initialTree, - initialHead: null, - initialCanonicalUrl, - initialSeedData: ['', null, children], - initialParallelRoutes, - isServer: false, - location: new URL('/linking', 'https://localhost') as any, - }) - const url = new URL('https://example.vercel.sh', 'https://localhost') const isExternalUrl = url.origin !== 'localhost' @@ -737,14 +703,9 @@ describe('navigateReducer', () => { locationSearch: '', navigateType: 'replace', shouldScroll: true, - mutable: {}, } - await runPromiseThrowChain(() => navigateReducer(state, action)) - - const newState = await runPromiseThrowChain(() => - navigateReducer(state2, action) - ) + const newState = await navigateReducer(state, action) expect(newState).toMatchInlineSnapshot(` { @@ -859,17 +820,6 @@ describe('navigateReducer', () => { ]) const state = createInitialRouterState({ - buildId, - initialTree, - initialHead: null, - initialCanonicalUrl, - initialSeedData: ['', null, children], - initialParallelRoutes, - isServer: false, - location: new URL('/linking', 'https://localhost') as any, - }) - - const state2 = createInitialRouterState({ buildId, initialTree, initialHead: null, @@ -887,14 +837,9 @@ describe('navigateReducer', () => { locationSearch: '', navigateType: 'push', shouldScroll: false, // should not scroll - mutable: {}, } - await runPromiseThrowChain(() => navigateReducer(state, action)) - - const newState = await runPromiseThrowChain(() => - navigateReducer(state2, action) - ) + const newState = await navigateReducer(state, action) expect(newState).toMatchInlineSnapshot(` { @@ -940,7 +885,31 @@ describe('navigateReducer', () => { "segmentPaths": [], }, "nextUrl": "/linking", - "prefetchCache": Map {}, + "prefetchCache": Map { + "/linking" => { + "data": Promise {}, + "kind": "temporary", + "lastUsedTime": 1690329600000, + "prefetchTime": 1690329600000, + "treeAtTimeOfPrefetch": [ + "", + { + "children": [ + "linking", + { + "children": [ + "__PAGE__", + {}, + ], + }, + ], + }, + undefined, + undefined, + true, + ], + }, + }, "pushRef": { "mpaNavigation": true, "pendingPush": true, @@ -1026,23 +995,12 @@ describe('navigateReducer', () => { location: new URL('/linking', 'https://localhost') as any, }) - await runPromiseThrowChain(() => prefetchReducer(state, prefetchAction)) + await prefetchReducer(state, prefetchAction) await state.prefetchCache.get(url.pathname + url.search)?.data - const state2 = createInitialRouterState({ - buildId, - initialTree, - initialHead: null, - initialCanonicalUrl, - initialSeedData: ['', null, children], - initialParallelRoutes, - isServer: false, - location: new URL('/linking', 'https://localhost') as any, - }) - - await runPromiseThrowChain(() => prefetchReducer(state2, prefetchAction)) - await state2.prefetchCache.get(url.pathname + url.search)?.data + await prefetchReducer(state, prefetchAction) + await state.prefetchCache.get(url.pathname + url.search)?.data const action: NavigateAction = { type: ACTION_NAVIGATE, @@ -1051,14 +1009,9 @@ describe('navigateReducer', () => { navigateType: 'push', locationSearch: '', shouldScroll: true, - mutable: {}, } - await runPromiseThrowChain(() => navigateReducer(state, action)) - - const newState = await runPromiseThrowChain(() => - navigateReducer(state2, action) - ) + const newState = await navigateReducer(state, action) const prom = Promise.resolve([ [ @@ -1162,7 +1115,7 @@ describe('navigateReducer', () => { "/linking/about" => { "data": Promise {}, "kind": "auto", - "lastUsedTime": null, + "lastUsedTime": 1690329600000, "prefetchTime": 1690329600000, "treeAtTimeOfPrefetch": [ "", @@ -1310,17 +1263,6 @@ describe('navigateReducer', () => { location: new URL('/parallel-tab-bar', 'https://localhost') as any, }) - const state2 = createInitialRouterState({ - buildId, - initialTree, - initialHead: null, - initialCanonicalUrl, - initialSeedData: ['', null, children], - initialParallelRoutes, - isServer: false, - location: new URL('/parallel-tab-bar', 'https://localhost') as any, - }) - const action: NavigateAction = { type: ACTION_NAVIGATE, url: new URL('/parallel-tab-bar/demographics', 'https://localhost'), @@ -1328,14 +1270,9 @@ describe('navigateReducer', () => { locationSearch: '', navigateType: 'push', shouldScroll: true, - mutable: {}, } - await runPromiseThrowChain(() => navigateReducer(state, action)) - - const newState = await runPromiseThrowChain(() => - navigateReducer(state2, action) - ) + const newState = await navigateReducer(state, action) expect(newState).toMatchInlineSnapshot(` { @@ -1428,7 +1365,39 @@ describe('navigateReducer', () => { ], }, "nextUrl": "/parallel-tab-bar/demographics", - "prefetchCache": Map {}, + "prefetchCache": Map { + "/parallel-tab-bar/demographics" => { + "data": Promise {}, + "kind": "temporary", + "lastUsedTime": 1690329600000, + "prefetchTime": 1690329600000, + "treeAtTimeOfPrefetch": [ + "", + { + "children": [ + "parallel-tab-bar", + { + "audience": [ + "__PAGE__", + {}, + ], + "children": [ + "__PAGE__", + {}, + ], + "views": [ + "__PAGE__", + {}, + ], + }, + ], + }, + null, + null, + true, + ], + }, + }, "pushRef": { "mpaNavigation": false, "pendingPush": true, @@ -1520,26 +1489,16 @@ describe('navigateReducer', () => { location: new URL('/linking#hash', 'https://localhost') as any, }) - const action: NavigateAction = { - type: ACTION_NAVIGATE, - url: new URL('/linking#hash', 'https://localhost'), - isExternalUrl: false, - locationSearch: '', - navigateType: 'push', + const mutable = { + canonicalUrl: '/linking#hash', + previousTree: initialTree, + hashFragment: '#hash', + pendingPush: true, shouldScroll: true, - mutable: { - canonicalUrl: '/linking#hash', - previousTree: initialTree, - hashFragment: '#hash', - pendingPush: true, - shouldScroll: true, - preserveCustomHistoryState: false, - }, + preserveCustomHistoryState: false, } - const newState = await runPromiseThrowChain(() => - navigateReducer(state, action) - ) + const newState = handleMutable(state, mutable) expect(newState).toMatchInlineSnapshot(` { @@ -1670,12 +1629,9 @@ describe('navigateReducer', () => { locationSearch: '', navigateType: 'push', shouldScroll: true, - mutable: {}, } - const newState = await runPromiseThrowChain(() => - navigateReducer(state, action) - ) + const newState = await navigateReducer(state, action) expect(newState).toMatchInlineSnapshot(` { diff --git a/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts b/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts index 83ef239c8bdba3..1c93c6c4c0713f 100644 --- a/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts +++ b/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts @@ -99,20 +99,14 @@ export function navigateReducer( state: ReadonlyReducerState, action: NavigateAction ): ReducerState { - const { url, isExternalUrl, navigateType, mutable, shouldScroll } = action + const { url, isExternalUrl, navigateType, shouldScroll } = action + const mutable: Mutable = {} const { hash } = url const href = createHrefFromUrl(url) const pendingPush = navigateType === 'push' // we want to prune the prefetch cache on every navigation to avoid it growing too large prunePrefetchCache(state.prefetchCache) - const isForCurrentTree = - JSON.stringify(mutable.previousTree) === JSON.stringify(state.tree) - - if (isForCurrentTree) { - return handleMutable(state, mutable) - } - mutable.preserveCustomHistoryState = false if (isExternalUrl) { diff --git a/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.test.tsx b/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.test.tsx index 8b30a15938e032..2622dd3a33e3cb 100644 --- a/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.test.tsx +++ b/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.test.tsx @@ -135,7 +135,6 @@ describe('refreshReducer', () => { }) const action: RefreshAction = { type: ACTION_REFRESH, - mutable: {}, origin: new URL('/linking', 'https://localhost').origin, } @@ -291,7 +290,6 @@ describe('refreshReducer', () => { const action: RefreshAction = { type: ACTION_REFRESH, - mutable: {}, origin: new URL('/linking', 'https://localhost').origin, } @@ -473,7 +471,6 @@ describe('refreshReducer', () => { const action: RefreshAction = { type: ACTION_REFRESH, - mutable: {}, origin: new URL('/linking', 'https://localhost').origin, } @@ -704,7 +701,6 @@ describe('refreshReducer', () => { const action: RefreshAction = { type: ACTION_REFRESH, - mutable: {}, origin: new URL('/linking', 'https://localhost').origin, } diff --git a/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts b/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts index f7fd84d839a8b3..3e317426fd16d2 100644 --- a/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts +++ b/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts @@ -3,6 +3,7 @@ import { createHrefFromUrl } from '../create-href-from-url' import { applyRouterStatePatchToTree } from '../apply-router-state-patch-to-tree' import { isNavigatingToNewRootLayout } from '../is-navigating-to-new-root-layout' import type { + Mutable, ReadonlyReducerState, ReducerState, RefreshAction, @@ -20,18 +21,12 @@ export function refreshReducer( state: ReadonlyReducerState, action: RefreshAction ): ReducerState { - const { mutable, origin } = action + const { origin } = action + const mutable: Mutable = {} const href = state.canonicalUrl let currentTree = state.tree - const isForCurrentTree = - JSON.stringify(mutable.previousTree) === JSON.stringify(currentTree) - - if (isForCurrentTree) { - return handleMutable(state, mutable) - } - mutable.preserveCustomHistoryState = false const cache: CacheNode = createEmptyCacheNode() diff --git a/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts b/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts index c6752fd1c06341..9f07e307f4e8cd 100644 --- a/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts +++ b/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts @@ -26,6 +26,7 @@ import type { ReadonlyReducerState, ReducerState, ServerActionAction, + ServerActionMutable, } from '../router-reducer-types' import { addBasePath } from '../../../add-base-path' import { createHrefFromUrl } from '../create-href-from-url' @@ -157,18 +158,12 @@ export function serverActionReducer( state: ReadonlyReducerState, action: ServerActionAction ): ReducerState { - const { mutable, resolve, reject } = action + const { resolve, reject } = action + const mutable: ServerActionMutable = {} const href = state.canonicalUrl let currentTree = state.tree - const isForCurrentTree = - JSON.stringify(mutable.previousTree) === JSON.stringify(currentTree) - - if (isForCurrentTree) { - return handleMutable(state, mutable) - } - mutable.preserveCustomHistoryState = false mutable.inFlightServerAction = fetchServerAction(state, action) diff --git a/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.test.tsx b/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.test.tsx index 7ab04b1ec09da1..a26219d84022e2 100644 --- a/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.test.tsx +++ b/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.test.tsx @@ -171,7 +171,6 @@ describe('serverPatchReducer', () => { true, ], overrideCanonicalUrl: undefined, - mutable: {}, } const newState = await runPromiseThrowChain(() => @@ -357,7 +356,6 @@ describe('serverPatchReducer', () => { true, ], overrideCanonicalUrl: undefined, - mutable: {}, } await runPromiseThrowChain(() => serverPatchReducer(state, action)) @@ -490,7 +488,6 @@ describe('serverPatchReducer', () => { locationSearch: '', navigateType: 'push', shouldScroll: true, - mutable: {}, } const state = createInitialRouterState({ @@ -526,7 +523,6 @@ describe('serverPatchReducer', () => { true, ], overrideCanonicalUrl: undefined, - mutable: {}, } const newState = await runPromiseThrowChain(() => diff --git a/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.ts b/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.ts index 0221aab10a100c..735cb1615db4d6 100644 --- a/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.ts +++ b/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.ts @@ -5,6 +5,7 @@ import type { ServerPatchAction, ReducerState, ReadonlyReducerState, + Mutable, } from '../router-reducer-types' import { handleExternalUrl } from './navigate-reducer' import { applyFlightData } from '../apply-flight-data' @@ -16,19 +17,9 @@ export function serverPatchReducer( state: ReadonlyReducerState, action: ServerPatchAction ): ReducerState { - const { flightData, previousTree, overrideCanonicalUrl, mutable } = action - - const isForCurrentTree = - JSON.stringify(previousTree) === JSON.stringify(state.tree) - - // When a fetch is slow to resolve it could be that you navigated away while the request was happening or before the reducer runs. - // In that case opt-out of applying the patch given that the data could be stale. - if (!isForCurrentTree) { - // TODO-APP: Handle tree mismatch - console.log('TREE MISMATCH') - // Keep everything as-is. - return state - } + const { flightData, overrideCanonicalUrl } = action + + const mutable: Mutable = {} if (mutable.previousTree) { return handleMutable(state, mutable) diff --git a/packages/next/src/client/components/router-reducer/router-reducer-types.ts b/packages/next/src/client/components/router-reducer/router-reducer-types.ts index 3dba27297705e4..dba908b0941dac 100644 --- a/packages/next/src/client/components/router-reducer/router-reducer-types.ts +++ b/packages/next/src/client/components/router-reducer/router-reducer-types.ts @@ -52,13 +52,11 @@ export interface ServerActionMutable extends Mutable { */ export interface RefreshAction { type: typeof ACTION_REFRESH - mutable: Mutable origin: Location['origin'] } export interface FastRefreshAction { type: typeof ACTION_FAST_REFRESH - mutable: Mutable origin: Location['origin'] } @@ -75,7 +73,6 @@ export interface ServerActionAction { actionArgs: any[] resolve: (value: any) => void reject: (reason?: any) => void - mutable: ServerActionMutable } /** @@ -115,7 +112,6 @@ export interface NavigateAction { locationSearch: Location['search'] navigateType: 'push' | 'replace' shouldScroll: boolean - mutable: Mutable } /** @@ -141,7 +137,6 @@ export interface ServerPatchAction { flightData: FlightData previousTree: FlightRouterState overrideCanonicalUrl: URL | undefined - mutable: Mutable } /** diff --git a/packages/next/src/shared/lib/router/action-queue.ts b/packages/next/src/shared/lib/router/action-queue.ts index 4310faba9caf39..54e57c76e6248c 100644 --- a/packages/next/src/shared/lib/router/action-queue.ts +++ b/packages/next/src/shared/lib/router/action-queue.ts @@ -80,7 +80,6 @@ async function runAction({ actionQueue.dispatch( { type: ACTION_REFRESH, - mutable: {}, origin: window.location.origin, }, setState