vercel / next.js

The React Framework
https://nextjs.org
MIT License
127.13k stars 27.02k forks source link

Dynamic Layout with Suspense hangs after 2nd navigation in Next 15 #72060

Open caleblloyd opened 3 weeks ago

caleblloyd commented 3 weeks ago

Link to the code that reproduces this issue

https://github.com/caleblloyd/next-layout-cache-bug/

To Reproduce

npm install
npm run dev

To Reproduce

Navigate to http://localhost:3000/now

  1. "Now Page" loads fine initially
  2. Click "Home Page"
  3. Click "Now Page", it is stuck at the suspense and never makes the API call

Video of bug starting from Now Page

If you start at the Home Page things work fine

Navigate to http://localhost:3000

  1. Click on "Now Page", it works
  2. Click on "Home Page"
  3. Click on "Now Page" again, it works

Video of things working starting from Home Page

Current vs. Expected behavior

I am using a layout in a subdirectory of the app router. It should be dynamic because I am using export const dynamic = 'force-dynamic' on the layout.

If I load a page that uses this layout initially, then navigate away and navigate back, it gets stuck at the Suspense and LoadData is never called again. next dev has the static route indicator on the page upon navigating back.

If I load a page that does not use this layout initially, I can navigate to a page using the layout multiple times and it works. next dev does not have the static route indicator on the page.

import {ReactNode, Suspense} from "react"
import Link from "next/link";

export const dynamic = 'force-dynamic'

async function LoadData(props: { children: ReactNode }) {
  const response = await fetch(`http://localhost:3000/api`)
  const data = await response.json() as { now: number }
  return <>
    {props.children}
    <div>Got API Response at {data.now}</div>
  </>
}

export default async function Layout(props: { children: ReactNode }) {
  return <>
    <div><Link href={`/`} className="text-blue-500">Home Page</Link></div>
    <Suspense fallback={<h1>Loading... {Date.now()}</h1>}>
      <LoadData>{props.children}</LoadData>
    </Suspense>
  </>
}

Provide environment information

Operating System:
  Platform: linux
  Arch: x64
  Version: #45-Ubuntu SMP PREEMPT_DYNAMIC Fri Aug 30 12:02:04 UTC 2024
  Available memory (MB): 15718
  Available CPU cores: 4
Binaries:
  Node: 20.18.0
  npm: 10.8.2
  Yarn: N/A
  pnpm: 9.12.1
Relevant Packages:
  next: 15.0.3-canary.1 // Latest available version is detected (15.0.3-canary.1).
  eslint-config-next: 15.0.2
  react: 19.0.0-rc-02c0e824-20241028
  react-dom: 19.0.0-rc-02c0e824-20241028
  typescript: 5.6.3
Next.js Config:
  output: N/A

Which area(s) are affected? (Select all that apply)

Not sure

Which stage(s) are affected? (Select all that apply)

next dev (local), next start (local)

Additional context

No response

caleblloyd commented 2 weeks ago

On further investigation, the layout never reloads in next start no matter which page is loaded initially.

As a workaround, I am able to remove the <Suspense> in the Layout, and use layout.tsx instead:

image

I still feel like this is a bug though, because the Loading UI and Streaming documentation seems to indicate that custom Suspense boundaries should work

Winwardo commented 6 days ago

Hi, I've been experiencing the same bug and it's blocked our upgrade from 14 -> 15.

EDIT: This does still repro with 15.0.4-canary.8, it just seems a bit less common, but that could just be randomness. I haven't been able to isolate a small repro repo like this issue, but navigating quickly through different pages on my project sometimes causes it to get stuck at Suspense boundaries inside the page, not in a Loading.tsx

Original, invalid

I've just tested again with 15.0.4-canary.8 and it appears to be fixed (I can once again navigate through my app back and forth without getting stuck at Suspense boundaries) I've looked through both NextJS' and React's changelogs and obviously cannot see which commit may have fixed this - are you still seeing this on your repro?