vercel / next.js

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

Nested layouts on a dynamic segment are re-mounted when navigating between them #44793

Open floatingdino opened 1 year ago

floatingdino commented 1 year ago

Verify canary release

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 21.6.0: Wed Aug 10 14:28:23 PDT 2022; root:xnu-8020.141.5~2/RELEASE_ARM64_T6000
Binaries:
  Node: 17.4.0
  npm: 8.3.1
  Yarn: N/A
  pnpm: 6.11.0
Relevant packages:
  next: 13.1.2-canary.5
  eslint-config-next: N/A
  react: 18.2.0
  react-dom: 18.2.0

Which area(s) of Next.js are affected? (leave empty if unsure)

App directory (appDir: true), Routing (next/router, next/navigation, next/link)

Link to the code that reproduces this issue

https://codesandbox.io/s/optimistic-forest-citt6o?file=/pages/index.tsx

To Reproduce

  1. Go to a route that uses nested + dynamic routes
  2. Enter text into an input in the outer and inner layout components
  3. Use next/link to visit another route that uses the same layout components

Describe the Bug

When using Next@13 app directory, layout components inside a dynamic route are re-mounted when navigating to a route with the same layout stack.

Expected Behavior

When navigating between two pages using the same layout stack, the layout is not re-mounted.

Which browser are you using? (if relevant)

No response

How are you deploying your application? (if relevant)

No response

Fredkiss3 commented 1 year ago

That is normal, since your layout is in the same folder as the dynamic segment, the layout will also depend on the parameter [slug], so if [slug] changes all the pages and layouts will also change.

You can see for yourself by adding a params: { slug } argument to your Layout, it will rerender everytime (on the server) :

// app/test/[slug]/layout.tsx
export default function TestLayout({ children, params: { slug } }) {
  console.log({ slug })
  return (
    <>
      <input className="border" />
      {children}
    </>
  );
}

If you want a Layout that persist between slug changes, you could either use the layout one level up in the tree :

.
└── app/
    └── test/
         ├── layout.tsx <-- this here
         └── [slug]/
              └── page.tsx

Or if you want a specific layout for your dynamic pages, you can create a route group :

.
└── app/
    └── test/
        ├── layout.tsx
        └── (group)/
             ├── layout.tsx <-- this here
             └── [slug]/
                  └── page.tsx
floatingdino commented 1 year ago

I see - do you know why it re-mounts the component rather than just passing down new props? In my specific use case I need access to the slug value inside the layout and I'd rather not extract if from the pathname if I can avoid it.

Fredkiss3 commented 1 year ago

The whole layout get unmounted and remounted, i think. You already have access to the slug in the layout and it is even passed to page.

HriBB commented 1 year ago

The whole layout get unmounted and remounted, i think. You already have access to the slug in the layout and it is even passed to page.

I have a similar problem. I have a canvas, which is unmounted and mounted on url change. I want to define a layout that has access to [...slugs] param, but wont re-mount on change. How can I achieve that?

Screencast from 2023-05-22 20-41-09.webm

ArjobanSingh commented 1 year ago

Hey @HriBB, Did you find any solution for this? I've same problem, I am using @monaco-editor/react on one of the children routes, but on every route change, it remounts, thus making the inner <Editor /> component to remount, which loads everytime and shows a loading flicker, similar to your canvas problem.

Did you find any workaround for preventing the child page's remount, rather only re-render.

HriBB commented 1 year ago

@ArjobanSingh I could not find a solution, so I switched to Remix, and got it working immediately.

M7ilan commented 10 months ago

Hello, I have the same issue but I use parallel routes. This is the stracture of my app:

[app]
    ├── globals.css
    ├── layout.tsx
    └── [route]
        └── [[[...slug]]]
            ├── [@Books]
                └── page.tsx
            ├── [@Content]
                └── page.tsx
            ├── [@Records]
                └── page.tsx
            ├── [@Title]
                └── page.tsx
            └── layout.tsx

Sample app https://issue-60037.vercel.app/

mira-hariri commented 3 months ago

@Fredkiss3 Hello, I am facing re-rending issue as well When running my Next app locally everything is working fine, but when I deployed it on vercel I faced re-rendering of the layout that is inside the route group this is my file structure image

the layout in the root file is working fine, but the layout in the (protected) group is re-rendering when navigating between the grouped pages (pets,home,posts,profile)