Fix browser navigation buttons not working with shallow routing and middleware (#43919)

This PR fixes #41064.
In some particular cases, while using a middleware and shallow routing
the navigation get stucks and stop refreshing the page. After futher
investigation it seems that a line of code was added that causes the
router pathname to be incorrect and then making Next believe it's the
same page that is loading.

## Bug

- [x] Related issues linked using `fixes #number`
- [x] Integration tests added
- [x] Errors have a helpful link attached, see
[`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md)

## Feature

- [ ] Implements an existing feature request or RFC. Make sure the
feature request has been accepted for implementation before opening a
PR.
- [ ] Related issues linked using `fixes #number`
- [ ]
[e2e](https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs)
tests added
- [ ] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
- [ ] Errors have a helpful link attached, see
[`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md)

## Documentation / Examples

- [ ] Make sure the linting passes by running `pnpm build && pnpm lint`
- [ ] The "examples guidelines" are followed from [our contributing
doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)

Co-authored-by: JJ Kasper <jj@jjsweb.site>
This commit is contained in:
Klein Torres 2022-12-13 05:02:06 +01:00 committed by GitHub
parent be90c0b909
commit a34b20e6f2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 109 additions and 9 deletions

View file

@ -1456,15 +1456,13 @@ export default class Router implements BaseRouter {
// we don't attempt resolve asPath when we need to execute
// middleware as the resolving will occur server-side
const isMiddlewareMatch = await matchesMiddleware({
asPath: as,
locale: nextState.locale,
router: this,
})
if (options.shallow && isMiddlewareMatch) {
pathname = this.pathname
}
const isMiddlewareMatch =
!options.shallow &&
(await matchesMiddleware({
asPath: as,
locale: nextState.locale,
router: this,
}))
if (isQueryUpdating && isMiddlewareMatch) {
shouldResolveHref = false

View file

@ -0,0 +1,9 @@
import { NextResponse } from 'next/server'
export async function middleware() {
return NextResponse.next()
}
export const config = {
matcher: '/(.*$)',
}

View file

@ -0,0 +1,26 @@
import Link from 'next/link'
import React from 'react'
const Page = () => {
return (
<>
<h1>Content for page 1</h1>
<div>
<Link
data-next-shallow-push
shallow
href={{ query: { params: 'testParams' } }}
>
Shallow push
</Link>
</div>
<div>
<Link data-next-page href="/page2">
Go to page 2
</Link>
</div>
</>
)
}
export default Page

View file

@ -0,0 +1,25 @@
import Link from 'next/link'
import React from 'react'
const Page = () => {
return (
<>
<h1>Content for page 2</h1>
<Link
data-next-shallow-replace
replace
shallow
href={{ query: { params: 'testParams' } }}
>
Shallow replace
</Link>
<div>
<button data-go-back onClick={() => window.history.back()}>
Go Back
</button>
</div>
</>
)
}
export default Page

View file

@ -0,0 +1,42 @@
import { createNext, FileRef } from 'e2e-utils'
import { NextInstance } from 'test/lib/next-modes/base'
import webdriver from 'next-webdriver'
import { join } from 'path'
describe('browser-shallow-navigation', () => {
let next: NextInstance
beforeAll(async () => {
next = await createNext({
files: {
pages: new FileRef(join(__dirname, 'app/pages')),
'middleware.js': new FileRef(join(__dirname, 'app/middleware.js')),
},
})
})
afterAll(() => next.destroy())
it('should render the correct page', async () => {
const browser = await webdriver(next.url, '/')
/// do shallow push
await browser.elementByCss('[data-next-shallow-push]').click()
await browser.waitForElementByCss('[data-next-page]')
// go to another page
await browser.elementByCss('[data-next-page]').click()
await browser.waitForElementByCss('[data-next-shallow-replace]')
// do shadow replace
await browser.elementByCss('[data-next-shallow-replace]').click()
await browser.waitForElementByCss('[data-go-back]')
// go back using history api
await browser.elementByCss('[data-go-back]').click()
// get page h1
let title = await browser.elementByCss('h1').text()
expect(title).toContain('Content for page 1')
})
})