Next-i18next: getInitialProps is not called unless _app.js extends next/app

Created on 14 Jan 2020  路  17Comments  路  Source: isaachinman/next-i18next

Describe the bug

getInitialProps is not called unless _app.js extends App from next/app. This prevents namespace splitting from working. This is a bug with next-i18next because next doesn't require App to be extended; next will call getInitialProps even when using a function component for _app.js. There examples use a function: https://nextjs.org/docs/advanced-features/custom-app

Occurs in next-i18next version

v4.0.0

Steps to reproduce

export a function component from _app.js instead of a class component that extends next/app.

Expected behaviour

I recognize that the docs for next-i18next use a class component, but it's not obvious that doing so is required, and perhaps it shouldn't be required since next doesn't require it.

Most helpful comment

So, I looked into this a bit. While there may indeed be issues ahead with hoisting, the main problem here is with the existence of getInitialProps on the _app component.

The example in this repo works, because extending next/app will add in a getIntialProps that then calls the actual page-level getInitialProps.

In fact, if you follow the second example in the exact NextJs docs that you linked to, you'll find that everything works as expected:

import React from 'react'
import App from 'next/app'
import { appWithTranslation } from '../i18n'

function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />
}

MyApp.getInitialProps = async (appContext) => {
  const appProps = await App.getInitialProps(appContext)
  return { ...appProps }
}

export default appWithTranslation(MyApp)

All 17 comments

Fair point. Not exactly sure how hoist-non-react-statics would work for non-class components. Hoping to learn something new here. PRs very welcome!

So, I looked into this a bit. While there may indeed be issues ahead with hoisting, the main problem here is with the existence of getInitialProps on the _app component.

The example in this repo works, because extending next/app will add in a getIntialProps that then calls the actual page-level getInitialProps.

In fact, if you follow the second example in the exact NextJs docs that you linked to, you'll find that everything works as expected:

import React from 'react'
import App from 'next/app'
import { appWithTranslation } from '../i18n'

function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />
}

MyApp.getInitialProps = async (appContext) => {
  const appProps = await App.getInitialProps(appContext)
  return { ...appProps }
}

export default appWithTranslation(MyApp)

Thanks for figuring that out.

please could you update the example repository
thanks

@chiqui3d Update it in what way?

The example didn't work for me, until I edited the _app.js file thanks to this issue, I almost went crazy.

https://github.com/isaachinman/next-i18next/blob/master/examples/simple/pages/_app.js

@chiqui3d The example is deployed and runs at next-i18next.com. It is also covered by multiple end-to-end tests. Can you be more specific?

So to apply Nextwith i18next, I have based on your example, and I did not work in any component pass props with the method getInitialProps, until I edited the file _app.js and I have added what you have commented here, as you can see in your example the file _app.js that does not include it.

MyApp.getInitialProps = async (appContext) => {
  const appProps = await App.getInitialProps(appContext)
  return { ...appProps }
}

This works for me after use withTranslation

    const { Component, pageProps } = this.props;
    if (Component.WrappedComponent) {
      Component.getInitialProps = Component.WrappedComponent.getInitialProps
    }

@isaachinman Is there a way to inject it in a lower-level component but not _app, because when wrapping _app with appWithTranslation it opted-out of Automatic Static Optimization. (Mind that I have tried to use it in a Main component which its a wrapper for navbar and pages, but did not work, it gives me the error http://localhost:5000/src/server/locales/ru/common.json 404 (Not Found))

@orifmilod same question to community!
How next.js docs (https://nextjs.org/docs/advanced-features/custom-app)
say:

Only uncomment this method if you have blocking data requirements for
// every single page in your application. This disables the ability to
// perform automatic static optimization, causing every page in your app to
// be server-side rendered.

// MyApp.getInitialProps = async (appContext) => {
// // calls page's getInitialProps and fills appProps.pageProps
// const appProps = await App.getInitialProps(appContext);
//
// return { ...appProps }
// }

Any ideas?
Please!

Modifying _app.js solved the issue I was having with trying to integrate next-i18next with next-with-apollo. Lucky I found this thread!

import App from 'next/app';

const MyApp = ({ Component, pageProps, apollo }) => (
  <ApolloProvider client={apollo}>
    <Component {...pageProps} />
  </ApolloProvider>
);

MyApp.getInitialProps = async (appContext) => {
  const appProps = await App.getInitialProps(appContext);
  return { ...appProps };
};

export default withApollo(appWithTranslation(MyApp));

Works the same way with withTranslation inside a certain page:

export default withApollo(withTranslation()(Collections));

@norbiu Be aware that this might results in Error: Circular structure in "getInitialProps".

It resolve issue, translations work - but build will have warnings related to getInitialProps in appWithTranslation HOC
https://github.com/isaachinman/next-i18next/issues/840

Not entirely sure, what I am facing here:

Screenshot 2020-12-01 at 10 12 45

Not entirely sure, what I am facing here:

Screenshot 2020-12-01 at 10 12 45

Alrighty.. I know what I did wrong here. Kept getting these errors, but again, as mentioned countless times over and over "Read the Docs carefully".

First mistake in my case, looking at the 404.tsx file. I am importing withTranslation from next-i18next. This was done automatically by VSCode, so didn't think more of it, but in reality, this should be fetched from your custom i18n.ts file. So it should actually read import { withTranslation } from '@/lib/i18n';.

Now - I would then realise I would face another issue, but that is solely due to Next.js. https://github.com/vercel/next.js/blob/master/errors/404-get-initial-props.md

Next.js does not permit getInitialProps for the custom 404.tsx file as this should serve 100% as a static file for fast delivery. Since I am not able to pass initial props to 404, then I would again get the missing namespacesRequired array warning in the console. I reckon we could either detect the filename and not display the warning as a condition thereof, or we should link to a solution on how to use the _error.tsx instead of having a static 404 file.

So, I looked into this a bit. While there may indeed be issues ahead with hoisting, the main problem here is with the existence of getInitialProps on the _app component.

The example in this repo works, because extending next/app will add in a getIntialProps that then calls the actual page-level getInitialProps.

In fact, if you follow the second example in the exact NextJs docs that you linked to, you'll find that everything works as expected:

import React from 'react'
import App from 'next/app'
import { appWithTranslation } from '../i18n'

function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />
}

MyApp.getInitialProps = async (appContext) => {
  const appProps = await App.getInitialProps(appContext)
  return { ...appProps }
}

export default appWithTranslation(MyApp)

Thank you! It works for me. My code:

type Props = {
  topMenu: TopNavigatorType[]
} & AppProps
function MyApp({ Component, pageProps, topMenu }: Props): JSX.Element {
  return (
    <>
      <AppProvider value={{ menu: topMenu }}>
        <Component {...pageProps} />
      </AppProvider>
      <script src="/lib/baidu.qiao.js" async defer />
    </>
  )
}

type Init = {
  topMenu: TopNavigatorType[]
} & AppInitialProps

MyApp.getInitialProps = async (appContext: AppContextType<Router>) => {
  const topMenu = await request.get(NAVIGATION)
  const appProps = await App.getInitialProps(appContext)
  const props: Init = { topMenu: topMenu.data, ...appProps }
  return { ...props }
}

export default MyApp
Was this page helpful?
0 / 5 - 0 ratings

Related issues

dimensi picture dimensi  路  4Comments

patsa picture patsa  路  6Comments

isaachinman picture isaachinman  路  7Comments

revskill10 picture revskill10  路  5Comments

nataliaroque77 picture nataliaroque77  路  3Comments