mirror of
https://github.com/hanzoai/next.js.git
synced 2026-09-24 17:00:51 +00:00
segment cache: add test for passing unawaited promise to context provider (#89339)
Adds a failing test proving that if the RSC response from a prefetch
contains an unresolved promise, navigating to the page will cause the
Suspense boundary wrapping the use of that promise to remain in fallback
state indefinitely.
This appears to have regressed in
e9a03ac12c, though the theory is that this
is just uncovering a React bug rather than causing the issue.
Closes #89170
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
import { ReactNode } from 'react'
|
||||
|
||||
export default function Root({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<html>
|
||||
<body>{children}</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { ReactNode } from 'react'
|
||||
import { getUser } from '../lib/get-user'
|
||||
import { UserProvider } from '../user-provider'
|
||||
|
||||
export default function LearnLayout({ children }: { children: ReactNode }) {
|
||||
const userPromise = getUser()
|
||||
|
||||
return <UserProvider userPromise={userPromise}>{children}</UserProvider>
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { UserCardShell } from '../user-card-shell'
|
||||
|
||||
export default async function LearnPage() {
|
||||
'use cache'
|
||||
|
||||
return (
|
||||
<main>
|
||||
<h1>Learn</h1>
|
||||
<UserCardShell />
|
||||
</main>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { cookies } from 'next/headers'
|
||||
|
||||
export async function getUser(): Promise<{ name: string }> {
|
||||
const cookieStore = await cookies()
|
||||
const userCookie = cookieStore.get('user')
|
||||
|
||||
if (!userCookie) {
|
||||
return { name: 'Guest' }
|
||||
}
|
||||
|
||||
return { name: userCookie.value }
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import Link from 'next/link'
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<main>
|
||||
<h1>User promise demo</h1>
|
||||
<Link href="/learn" id="learn-link">
|
||||
Go to learn
|
||||
</Link>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
'use client'
|
||||
|
||||
import { Suspense } from 'react'
|
||||
import { UserCard } from './user-card'
|
||||
|
||||
export function UserCardShell() {
|
||||
return (
|
||||
<Suspense fallback={<p id="fallback">loading</p>}>
|
||||
<UserCard />
|
||||
</Suspense>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
'use client'
|
||||
|
||||
import { useUser } from './user-provider'
|
||||
|
||||
export function UserCard() {
|
||||
const user = useUser()
|
||||
|
||||
return <p id="user">user: {user?.name ?? 'unknown'}</p>
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
'use client'
|
||||
|
||||
import { createContext, ReactNode, use } from 'react'
|
||||
|
||||
type User = { name: string } | undefined
|
||||
|
||||
const UserContext = createContext<Promise<User> | null>(null)
|
||||
UserContext.displayName = 'UserContext'
|
||||
|
||||
export function UserProvider({
|
||||
children,
|
||||
userPromise,
|
||||
}: {
|
||||
children: ReactNode
|
||||
userPromise: Promise<User>
|
||||
}) {
|
||||
return (
|
||||
<UserContext.Provider value={userPromise}>{children}</UserContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export function useUser(): User {
|
||||
const userPromise = use(UserContext)
|
||||
if (!userPromise) {
|
||||
throw new Error('useUser must be used within a UserProvider')
|
||||
}
|
||||
|
||||
return use(userPromise)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* @type {import('next').NextConfig}
|
||||
*/
|
||||
const nextConfig = {
|
||||
cacheComponents: true,
|
||||
}
|
||||
|
||||
module.exports = nextConfig
|
||||
@@ -0,0 +1,17 @@
|
||||
import { nextTestSetup } from 'e2e-utils'
|
||||
import { retry } from 'next-test-utils'
|
||||
|
||||
describe('prefetch-partial-rsc', () => {
|
||||
const { next } = nextTestSetup({
|
||||
files: __dirname,
|
||||
})
|
||||
|
||||
it('resolves after a client-side navigation', async () => {
|
||||
const browser = await next.browser('/')
|
||||
await browser.elementByCss('#learn-link').click()
|
||||
await retry(async () => {
|
||||
const text = await browser.elementByCss('#user').text()
|
||||
expect(text).toBe('user: Guest')
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user