47ng/nuqs

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

luixo opened this issue · 5 comments

luixo commented

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:

  • ❌ The app router
  • ✅ The pages router
  • ❌ The basePath option in your Next.js config
  • ❌ The experimental windowHistorySupport flag in your Next.js config

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.

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

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

Resolved here: trpc/trpc#5133

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

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