47ng / nuqs

Type-safe search params state manager for Next.js - Like React.useState, but stored in the URL query string.
https://nuqs.47ng.com
MIT License
3.45k stars 70 forks source link

Different instances of `next/navigation` on SSR with tRPC #424

Closed luixo closed 2 weeks ago

luixo commented 9 months ago

Context

What's your version of next-usequerystate?

1.13.0

Next.js information (obtained by running next info):

14.0.3

Are you using:

Description

My setup is Next.js 14 + tRPC (which runs some SSR prepasses with a corresponding attribute on). The SSR prepass is the location when it goes wrong - app crashes with invariant expected app router to be mounted error (which basically means we don't have AppRouterContext in react scope). This only applies to SSR, if I render page without useQueryState hook on SSR and navigate to the problematic page client-side - there is not problem.

It happens because there are two different contexts - one from "next/navigation" (the one that actually exists in scope) and one from "next/navigation.js" (the one expected by hook in useQueryState).

To illustrate this example, I wrote this code in my custom hook:

import { useRouter } from "next/navigation";
import { useRouter as useRouterJs } from "next/navigation.js";

console.log("Are the same?", useRouterJs === useRouter); // false server-side, true client-side

My Node.js debugger links those functions to different instances of navigation.js file, one has link webpack-internal:///node_modules/next/dist/client/components/navigation.js and another has file:///<my-app>/node_modules/next/dist/client/components/navigation.js.

I suspect something's happening in the bundling process.

My current (temporary) solution is to patch the library and pass my own useRouter and useSearchParams, but it is not sustainable.

Reproduction

  1. Create a tRPC Next.js template
  2. Add ssr: true in createTRPCNext function
  3. Render the page with useQueryState hook

This is connected to a change requested in this discussion.

franky47 commented 9 months ago

I'm getting a different error with a simple create-next-app + tRPC with ssr: true setup:

TypeError: useInsertionEffect only works in Client Components. Add the "use client" directive at the top of the file to use it. Read more: https://nextjs.org/docs/messages/react-client-hook-in-server-component

This is odd since we're in the pages router, there should be no concept of client or server components there. Since the error goes away when setting ssr: false, it could be related to your issue, throwing webpack off.

Do you have a minimal reproduction example where you see your import issue?

luixo commented 9 months ago

I'm getting a different error with a simple create-next-app + tRPC with ssr: true setup

Resolved here: https://github.com/trpc/trpc/issues/5133

franky47 commented 9 months ago

Thanks for the patch, I'll give it a try tomorrow.

I also tried foregoing the use of useInsertionEffect in #429, but it gets trickier to sync the state of hooks with Next.js' navigation using traditional useEffects, because of the rendering order, so I probably won't explore this idea further.

luixo commented 9 months ago

There's no need to do that, tRPC uses an obsolete library react-ssr-prepass to prerender non-suspense hooks at the moment, it's not on this library side

franky47 commented 2 weeks ago

Hey, chiming in to see if this issue still needs looking into? Otherwise I'll close it.

luixo commented 2 weeks ago

I believe the bug might still be there, but I personally moved away from ssr prerender in tRPC so I don't stumble upon that anymore.

franky47 commented 2 weeks ago

Thanks, I'll close the issue for now. If someone else stumbles upon it, feel free to chime in with reproductions steps so I can take a closer look.