Files
05cd5157e5 app router: Fix infinite redirect loop in MPA navigation (#49058)
Not 100% convinced that this is the correct fix but it's the best I can
think of.

Previously, we would sometimes call location.assign()/.replace()
hundreds of times (or more) as I described in
https://github.com/vercel/next.js/issues/48438 and
https://github.com/vercel/next.js/issues/48309#issuecomment-1512290958.
Sometimes this would just make things slow but the navigation would
eventually succeed; sometimes this would just hang the browser.

Now we trigger it only once (or—for a reason I don't quite
understand—twice in dev, as you can see in the test) and never commit
that render.

This also fixes the bug I mentioned in
https://github.com/vercel/next.js/issues/48438#issuecomment-1528649776
where usePathname() and useSearchParams() would return the page we are
navigating to (even if that's an external page wholly unrelated to our
site!).

Fixes #48309, fixes #48438.

link NEXT-1028

Co-authored-by: Jimmy Lai <[email protected]>
2023-05-03 11:04:53 +02:00

72 lines
2.0 KiB
JavaScript

/*global navigation*/
'use client'
import { usePathname, useRouter, useSearchParams } from 'next/navigation'
import { useEffect, useTransition } from 'react'
let listening = false
let startedNavigating = false
export default function Page({ params: { storageKey } }) {
if (typeof window === 'undefined') {
throw new Error('Client render only')
}
let router = useRouter()
let path = usePathname()
let searchParams = useSearchParams().toString()
if (searchParams) {
path += `?${searchParams}`
}
// Log every pathname+searchParams we've seen
sessionStorage.setItem(`${storageKey}/path-${path}`, 'true')
let [isPending, startTransition] = useTransition()
useEffect(() => {
if (startedNavigating) {
sessionStorage.setItem(`${storageKey}/lastIsPending`, isPending)
}
})
// Read all matching logs and print them
let storage = Object.fromEntries(
Object.entries(sessionStorage).flatMap(([key, value]) =>
key.startsWith(`${storageKey}/`)
? [[key.slice(storageKey.length + 1), value]]
: []
)
)
return (
<>
<button
id="go"
onClick={() => {
// Count number of navigations triggered (in browsers that support it)
sessionStorage.setItem(
`${storageKey}/navigation-supported`,
typeof navigation !== 'undefined'
)
if (!listening && typeof navigation !== 'undefined') {
listening = true
navigation.addEventListener('navigate', (event) => {
let key = `${storageKey}/navigate-${event.destination.url}`
let count = +sessionStorage.getItem(key)
sessionStorage.setItem(key, count + 1)
})
}
startedNavigating = true
startTransition(() => {
router.push('https://example.vercel.sh/stuff?abc=123')
})
}}
>
go
</button>
<pre id="storage">{JSON.stringify(storage, null, 2)}</pre>
</>
)
}