47ng / nuqs

Type-safe search params state manager for Next.js - Like React.useState, but stored in the URL query string.

Home Page:https://nuqs.47ng.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Fast Refresh had to perform a full reload (dev only)

tacomanator opened this issue · comments

Reproducible example:

https://github.com/tacomanator/firstclick

  1. Run the example in development mode
  2. Navigate to "Pages2." It imports imports component using next/router useRouter(). Loads without issue
  3. Navigate to "Pages." It imports component using useQueryState(). Page does not load on first click. In the terminal, "Fast Refresh had to perform a full reload." reported instead. If removing useQueryState() the problem goes away.

May not be an issue with this library, but able to reproduce by using this library so giving it a shot. Only occurs in development mode. Also, does not occur on pages in the pages folder and not the app router.

Thanks a lot for the reproduction repository, unfortunately I don't see the described behaviour on my end:

repro.mov

Terminal output:

  ▲ Next.js 13.5.4
  - Local:        http://localhost:3000

 ✓ Ready in 1119ms
 ○ Compiling /page ...
 ✓ Compiled /page in 5.5s (482 modules)
 ✓ Compiled /pagesrouter2 in 1580ms (728 modules)
 ✓ Compiled /favicon.ico/route in 490ms (385 modules)
 ✓ Compiled /pagesrouter in 540ms (763 modules)

Very very sorry—my reproduction steps were wrong. Apparently it only occurs when Pages is the first link visited, so steps should have been in reverse order or better yet should have involved restarting the dev server:

  1. Run the example in development mode
  2. Navigate to "Pages2." It imports imports component using next/router useRouter(). Loads without issue
  3. Stop server and restart in development mode
  4. Navigate to "Pages." It imports component using useQueryState(). Page does not load on first click. In the terminal, "Fast Refresh had to perform a full reload." reported instead. If removing useQueryState() the problem goes away.

This is how it goes for me:

firstclick.mov

Terminal:

  ▲ Next.js 13.5.4
  - Local:        http://localhost:3000

 ✓ Ready in 2.4s
 ✓ Compiled /page in 394ms (468 modules)
 ⚠ Fast Refresh had to perform a full reload. Read more: https://nextjs.org/docs/messages/fast-refresh-reload
 ✓ Compiled /pagesrouter in 602ms (727 modules)
 ✓ Compiled /favicon.ico/route in 218ms (386 modules)
 ✓ Compiled /pagesrouter2 in 171ms (763 modules)

I was able to reproduce the example, thanks.

I believe the full reload may be due to the fact that importing next-usequerystate has a side-effect of patching the History API. This only happens in dev mode because those imports are only evaluated just before a page is first visited.

You may wonder "but wait, the app route also imports next-usequerystate", so the side-effects should have been handled there first? It looks like both routers live in different bundles/memory spaces with regards to imports, which causes those side-effects to re-run every time we cross the app/pages boundary...

CleanShot 2023-10-10 at 23 12 59@2x

This is something I started working on last week: a way to detect whether patching was applied by storing this "patched/unpatched" state inside the window.history object itself. But since the even emitter that it's calling when a history event occurs probably also gets reset and thrown away with the module cache on router boundary crossings, it looks like patching is needed every time to connect to the right place.

I thought this could be a memory leak, as the history object gets infinitely patched over and over again, but it looks like Next does a hard reload on router boundary crossings, which resets the history object.

TL;DR: router boundary crossings are messy. Better avoid them if possible.

OK, thanks for looking into it! More of an annoyance than anything else. Not just this but in general I find "incremental adoption" of the app router touted by next.js to be fraught with minefields in reality (all but impossible when adding in many depdencies), so getting used to encountering these edge cases.

Agreed. I ripped the band-aid and ported it all at once on my own website, but not something I'd do with my other production Next.js apps.

For now1, your demo works fine with all combinations of app/pages and n-uqs/router in relation to query edition, so I'll go ahead and close this issue which is more related to a Next.js behaviour.

Footnotes

  1. https://github.com/vercel/next.js/issues/56636