Skip to content

Running Under NODE_ENV=test Errors with "NextRouter was not mounted" #40

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
nbibler opened this issue Nov 8, 2023 · 5 comments
Open
Labels
bug Something isn't working

Comments

@nbibler
Copy link

nbibler commented Nov 8, 2023

Describe the bug

Standing up a new NextJS application, using the Pages router, adding this library, and running under NODE_ENV=test errors immediately with:

Error: NextRouter was not mounted. https://nextjs.org/docs/messages/next-router-not-mounted

Object.useRouter         in /node_modules/next/dist/client/router.js  (146:15)
exports.NextAdapterPages in /node_modules/next-query-params/dist/NextAdapterPages-b4b86ccd.js (1:293)

This does not appear to be an issue when NODE_ENV=development nor NODE_ENV=production (via npm run build && NODE_ENV=production npm run start.

To Reproduce

  1. npx create-next-app@13.5.6, do not use the App Router and switch into the new directory,
  2. npm install --save next-query-params use-query-params,
  3. Modify the _app.tsx to:
 import '@/styles/globals.css'
 import type { AppProps } from 'next/app'
+import NextAdapterPages from "next-query-params/pages";
+import { QueryParamProvider } from "use-query-params";
 
 export default function App({ Component, pageProps }: AppProps) {
-  return <Component {...pageProps} />
+  return (
+    <QueryParamProvider adapter={NextAdapterPages}>
+      <Component {...pageProps} />
+    </QueryParamProvider>
+  );
 }
  1. Run the app under NODE_ENV=test npm run dev

Expected behavior

There should not be a NextRouter error and the application should display the generic NextJS welcome page.

@nbibler nbibler added the bug Something isn't working label Nov 8, 2023
@nbibler
Copy link
Author

nbibler commented Nov 8, 2023

This error also occurs in NextJS 14.0.1 using the same steps provided above, but with npx create-next-app@latest using the Pages router.

@n-d-r-d-g
Copy link

Hi @nbibler,

I was experiencing the same issue. I fixed it by upgrading the following packages:

"next-query-params": "^5.0.0",
"use-query-params": "^2.2.1"

Note: I'm using "next": "14.0.4".

@mifrej
Copy link

mifrej commented Jan 4, 2024

I'm having this issue even with the above versions mentioned by @n-d-r-d-g

@klausbadelt
Copy link

This doesn't just happen with NODE_ENV=test. @n-d-r-d-g 's suggestion doesn't fix. We're on next@14.1.0 and next-query-params@5.0.0

Seems router.isReady should checked before using the router here:

const match = router.asPath.match(pathnameRegex);

Our workaround is currently to use a custom wrapper around QueryParamProvider like this:

export const NextQueryParamProvider = ({ children }) => {
  const router = useRouter()

  return router.isReady ? (
    <QueryParamProvider adapter={NextAdapterPages} options={{ removeDefaultsFromUrl: true }}>
      {children}
    </QueryParamProvider>
  ) : children
}

Hopefully we'll have time to submit a PR. Thanks for a very useful library @amannn !

@l0gicgate
Copy link

l0gicgate commented Oct 16, 2024

@klausbadelt Your workaround doesn't work in our app unfortunately.

In our app, this only occurs in NODE_ENV=TEST using next 14.2.15

After further investigation, it appears that there's an issue with the way this package is bundled.

The RequireJS version of this package fails in dev. If I simply replace the source from the b4b86ccd bundle with the 293777d9 bundle which is ES6, the error disappears.

The one bundle uses require while the other one uses import. It's the only visible difference.

b4b86ccd bundle uses require (doesn't work):
CleanShot 2024-10-15 at 22 05 23@2x

293777d9 bundle uses import (works):
CleanShot 2024-10-15 at 22 06 12@2x

CleanShot 2024-10-15 at 21 55 00@2x

For anyone looking for a temporary workaround, you'll need to bring in the pages adapter code from this repo:

import { useRouter } from 'next/router';
import type { ReactElement } from 'react';
import { useMemo } from 'react';
import type { PartialLocation, QueryParamAdapter } from 'use-query-params';
import { QueryParamProvider as UseQueryParamProvider } from 'use-query-params';

const pathnameRegex = /[^?#]+/u;

const Adapter = ({
  children,
  shallow = true,
}: {
  shallow?: boolean;
  children(adapter: QueryParamAdapter): ReactElement | null;
}) => {
  const router = useRouter();
  const match = router.isReady ? router.asPath.match(pathnameRegex) : null;
  const pathname = match ? match[0] : router.asPath;

  const location = useMemo(() => {
    if (typeof window === 'undefined') {
      // On the server side we only need a subset of the available
      // properties of `Location`. The other ones are only necessary
      // for interactive features on the client.
      return { search: router.asPath.replace(pathnameRegex, '') } satisfies Partial<Location>;
    } else {
      // For SSG, no query parameters are available on the server side,
      // since they can't be known at build time. Therefore, to avoid
      // markup mismatches, we need a two-part render in this case that
      // patches the client with the updated query parameters lazily.
      // Note that for SSR `router.isReady` will be `true` immediately
      // and therefore there's no two-part render in this case.
      if (router.isReady) {
        return window.location;
      } else {
        return { search: '' } satisfies Partial<Location>;
      }
    }
  }, [router.asPath, router.isReady]);

  const adapter: QueryParamAdapter = useMemo(() => {
    function createUpdater(routeFn: typeof router.push) {
      return function updater({ hash, search }: PartialLocation & { hash?: string }) {
        void routeFn(
          { pathname: router.pathname, search, hash },
          { pathname, search, hash },
          { shallow, scroll: false },
        );
      };
    }

    return {
      push: createUpdater(router.push),
      replace: createUpdater(router.replace),
      location,
    };
  }, [location, pathname, router, shallow]);

  return children(adapter);
};

export const QueryParamProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => (
  <UseQueryParamProvider adapter={Adapter}>{children}</UseQueryParamProvider>
);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants