Skip to content

Commit

Permalink
memoize layout router context
Browse files Browse the repository at this point in the history
  • Loading branch information
ztanner committed Apr 16, 2024
1 parent 3cf4183 commit a6967ed
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 17 deletions.
40 changes: 23 additions & 17 deletions packages/next/src/client/components/app-router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,27 @@ function Router({
return getSelectedParams(tree)
}, [tree])

const layoutRouterContext = useMemo(() => {
return {
childNodes: cache.parallelRoutes,
tree,
// Root node always has `url`
// Provided in AppTreeContext to ensure it can be overwritten in layout-router
url: canonicalUrl,
loading: cache.loading,
}
}, [cache.parallelRoutes, tree, canonicalUrl, cache.loading])

const globalLayoutRouterContext = useMemo(() => {
return {
buildId,
changeByServerResponse,
tree,
focusAndScrollRef,
nextUrl,
}
}, [buildId, changeByServerResponse, tree, focusAndScrollRef, nextUrl])

let head
if (matchingHead !== null) {
// The head is wrapped in an extra component so we can use
Expand Down Expand Up @@ -668,25 +689,10 @@ function Router({
<PathnameContext.Provider value={pathname}>
<SearchParamsContext.Provider value={searchParams}>
<GlobalLayoutRouterContext.Provider
value={{
buildId,
changeByServerResponse,
tree,
focusAndScrollRef,
nextUrl,
}}
value={globalLayoutRouterContext}
>
<AppRouterContext.Provider value={appRouter}>
<LayoutRouterContext.Provider
value={{
childNodes: cache.parallelRoutes,
tree,
// Root node always has `url`
// Provided in AppTreeContext to ensure it can be overwritten in layout-router
url: canonicalUrl,
loading: cache.loading,
}}
>
<LayoutRouterContext.Provider value={layoutRouterContext}>
{content}
</LayoutRouterContext.Provider>
</AppRouterContext.Provider>
Expand Down
23 changes: 23 additions & 0 deletions test/e2e/app-dir/app-prefetch/app/with-error/error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
'use client'

export default function Error({
error,
reset,
}: {
error: Error & {
digest?: string
statusCode: number
}
reset: () => void
}) {
console.log('error render')

return (
<main>
<h1>Error</h1>
<div id="random-number">Random: {Math.random()}</div>
<p>{error.message}</p>
<button onClick={reset}>Reset</button>
</main>
)
}
16 changes: 16 additions & 0 deletions test/e2e/app-dir/app-prefetch/app/with-error/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
'use client'

import { useState } from 'react'

/** Add your relevant code here for the issue to reproduce */
export default function Page() {
const [error, setError] = useState(false)
if (error) {
throw new Error('This is a test error')
}
return (
<>
<button onClick={() => setError(true)}>Throw Error</button>
</>
)
}
17 changes: 17 additions & 0 deletions test/e2e/app-dir/app-prefetch/prefetching.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,23 @@ createNextDescribe(
expect(prefetchResponse).toContain('Loading Prefetch Auto')
})

it('should not re-render error component when triggering a prefetch action', async () => {
const browser = await next.browser('/with-error')

const initialRandom = await browser
.elementByCss('button')
.click()
.waitForElementByCss('#random-number')
.text()

await browser.eval('window.next.router.prefetch("/")')

// confirm the error component was not re-rendered
expect(await browser.elementById('random-number').text()).toBe(
initialRandom
)
})

describe('dynamic rendering', () => {
describe.each(['/force-dynamic', '/revalidate-0'])('%s', (basePath) => {
it('should not re-render layout when navigating between sub-pages', async () => {
Expand Down

0 comments on commit a6967ed

Please sign in to comment.