next-translate icon indicating copy to clipboard operation
next-translate copied to clipboard

Dont`t consume NextJs useRouter hook in I18nProvider

Open gurkerl83 opened this issue 2 years ago • 5 comments

The current implementation of NextJs useRouter hook returns the entire context object as a consumer.

export function useRouter(): NextRouter {
  return React.useContext(RouterContext)
}

Part of the context is the property asPath which includes the query and hash shown in the browser. For in-page navigation purposes, e.g., using a table of content, the hash gets used to navigate to the respective page sections.

Changing minor navigation aspects like the should not result in a change of asPath, but in NextJs it results in a different context. Based on the useRouter implementation, a re-rendering occurs.

Using the NextJs useRouter hook in I18nProvider, the consequence is that the Provider re-creates itself anytime a minor in-page navigation gets triggered.

To avoid this, provide the values from the Next context required within I18nProvider from the outside like that.

Memo in _app


const { __namespaces, __lang } = pageProps;

const {
   query: { slug },
   locale,
   defaultLocale,
} = useRouter();

const component = useMemo(() => {
   return (
      <I18nProvider
         lang={__lang}
         namespaces={__namespaces}
         // provide relevant properties from the outside
         // tested locally and it seems those are not required, even when removing useRouter from I18nProvider
         locale={locale}
         defaultLocale={defaultLocale}
      >
         <Component {...pageProps} />
      </I18nProvider>
   );
}, [slug, locale]); // re-create the component instance when slug or locale changes

The above example relies on loading relevant namespaces in getStaticProps, here for dynamic pages, which works really good.

export const getStaticProps: GetStaticProps<Props> = async (ctx) => {
   const { params: { slug } = { slug: [] }, locale } = ctx;

   const pathname = path.sep.concat(
      Array.isArray(slug) ? slug.join(path.sep) : slug
   );

   const translation = await loadNamespaces({
      ...i18nConfig,
      pathname,
      locale
   });

   return {
      props: {
         ...translation
      }
   };
};

Can you please elaborate a constellation where properties of useRouter are actually relevant in I18nProvider? I have removed the hook and those properties entirely from I18nProvider and it still works perfectly.

Thx!

gurkerl83 avatar Apr 11 '22 08:04 gurkerl83

Feel free to PR @gurkerl83 ! Thanks for your proposal!

aralroca avatar Feb 20 '23 17:02 aralroca

Not sure if the same but with nextjs 13.5 i am getting the same (and strangely only with NODE_ENV=test):

Error: NextRouter was not mounted. https://nextjs.org/docs/messages/next-router-not-mounted at useRouter (C:\app\client\node_modules\next\dist\client\router.js:146:15) at I18nProvider (C:\app\client\node_modules\next-translate\lib\cjs\I18nProvider.js:50:37)

downgrading to nextjs 13.4.19 solves it

gittygoo avatar Oct 04 '23 22:10 gittygoo

@gittygoo I am facing the same problem

BjoernRave avatar Oct 20 '23 10:10 BjoernRave

I get the same issue with next 14 with or without NODE_ENV=test

cykoder avatar Oct 27 '23 20:10 cykoder