Skip to content
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

Functional _app.js causes page getInitialProps to not run #9284

Closed
jaydenseric opened this issue Nov 2, 2019 · 12 comments · Fixed by #9287
Closed

Functional _app.js causes page getInitialProps to not run #9284

jaydenseric opened this issue Nov 2, 2019 · 12 comments · Fixed by #9287
Assignees
Milestone

Comments

@jaydenseric
Copy link
Contributor

Bug report

Describe the bug

Exporting a function App in pages/_app.js as documented in #9268 causes the getInitialProps of pages to stop working.

To Reproduce

Create a custom app as documented here: https://github.com/zeit/next.js/tree/v9.1.3-canary.1#custom-app

const CustomApp = ({ Component, pageProps }) => <Component {...pageProps} />

export default CustomApp

In a page, try using getInitialProps:

IndexPage.getInitialProps = () => {
  console.log('Works!')
  return {}
}

Expected behavior

The page's getInitialProps should run when the page is loaded, in this example resulting in Works! being logged to the console.

System information

  • Version of Next.js: v9.1.3-canary.1

Additional context

Original issue for the functional app feature: #7515

@jhvissotto
Copy link

I have exactly the same issue today. Can fix this issue, please?

@jhvissotto
Copy link

I solved my problem now using this preconfiguration, I hope help you.

import App from 'next/app'

export default class MyApp extends App {
    static async getInitialProps({ Component, ctx }) {
        let pageProps = {}
    
        if (Component.getInitialProps) {
            pageProps = await Component.getInitialProps({ ctx })
        }

        // GET_INITIAL_PROPS_CODE_INI
        //
        // bla bla bla....
        //
        // GET_INITIAL_PROPS_CODE_END

        return ({ pageProps })
    }
  
    render() {
        const { Component, pageProps } = this.props
        return <Component {...pageProps} />
    }
}

@timneutkens
Copy link
Member

As you can see there are tests to ensure it works correctly: https://github.com/zeit/next.js/pull/9287/files#diff-a6943712c01ef0dec9f061ad7e0fe8f9

Make sure you're on the latest version of Next.js.

@esseswann
Copy link

I confirm this bug
Next 9.3.4

@timneutkens
Copy link
Member

@esseswann how can you confirm a bug that has tests for the exact case outlined in the initial issue template.

Please create a new issue following the issue template and provide a complete reproduction

@esseswann
Copy link

Man, as programmer I understand your frustration but tests can also have bugs:
Here is the only thing I needed to do so pages down the tree were able to receive something in getInitialProps. With functional component pageProps were empty while using MyApp.getInitialProps
image
What do you need for complete reproduction? Is access to our repo acceptable to you?

@timneutkens
Copy link
Member

Please provide your _app.js and the page you're trying to access.

@esseswann
Copy link

I think I know what the problem is, it's ApolloClient

_app.js

import { createElement as $ } from 'react'
import withApolloClient from '../lib/with-apollo-client'

const MyApp = ({
  Component,
  pageProps // this is empty
}) =>
  $(Component, pageProps)

export default withApolloClient(MyApp)

index.js

import { createElement as $ } from 'react'

const Index = ({ host }) =>
  $('div', null, 'host is: ', host)

Index.getInitialProps = async ctx => {
  console.log(ctx.req) // this does not fire
  return { host:ctx.req.headers.host.split('.') }
}

export default Index

with-apollo-client.js

import React from 'react'
import initApollo from './init-apollo'
import Head from 'next/head'
import { getDataFromTree } from '@apollo/react-ssr'

export default App =>
  class Apollo extends React.Component {
    static displayName = 'withApollo(App)'
    static async getInitialProps (ctx) {
      const { AppTree } = ctx
      let headers
      
      let appProps = {}
      if (App.getInitialProps) {
        appProps = await App.getInitialProps(ctx)
      }

      // Run all GraphQL queries in the component tree
      // and extract the resulting data
      const apollo = initApollo({ headers })
      if (typeof window === 'undefined') {
        try {
          // Run all GraphQL queries
          await getDataFromTree(<AppTree {...appProps} apolloClient={apollo} />)
        } catch (error) {
          // Prevent Apollo Client GraphQL errors from crashing SSR.
          // Handle them in components via the data.error prop:
          // https://www.apollographql.com/docs/react/api/react-apollo.html#graphql-query-data-error
          console.error('Error while running `getDataFromTree`', error)
        }

        // getDataFromTree does not call componentWillUnmount
        // head side effect therefore need to be cleared manually
        Head.rewind()
      }

      // Extract query data from the Apollo store
      const apolloState = apollo.cache.extract()

      return {
        ...appProps,
        apolloState
      }
    }

    constructor (props) {
      super(props)
      this.apolloClient = initApollo(props.apolloState)
    }

    render () {
      return <App apolloClient={this.apolloClient} {...this.props} />
    }
  }

@ksorv
Copy link

ksorv commented Jun 8, 2020

+1, May be adding more docs around it'll help!

@Timer
Copy link
Member

Timer commented Jun 8, 2020

@ksorv this bug was fixed in Next.js 9.1.3 and newer.

@ksorv
Copy link

ksorv commented Jun 9, 2020

@Timer Indeed. That's why I said more docs[or better] will certainly help!

For anyone else coming around here:

getInitialProps in _app.js =>
NextJS docs almost tells you not to use them as they turn Automatic Static Optimization Off.
& that when you use this, for some reason it turns off Page's getInitialProps!

But if you have to use them anyways, then:

You've to call Page's getInitialProps in _app.js's getInitialProps as:

class CustomApp extends App{
     ...

    // Docs[of _app] does say that you can use Component in _app.js but in `getInitialProps` it doesn't say so/
    static async getInitialProps({Component, ctx}){
        let pageProps,
        if(Component.getInitialProps) {
            // Pass it ctx if you have to, idk
            pageProps = await Component.getInitialProps()
        }
        ...
        return {...pageProps, ...}
    }

    render(){
        return (
                <Provider>
                <Component {...pageProps}></Component>
                </Provider>
    }
}

And now do everything normally as you would!
i.e, Define getInitialProps in pages, and see you're able to use them.

P.S. Don't try to use them in components it wont work.

Unless you do something like:

const HOC = (PassedComponent)=> {
    return class Some extends Component{
        static async getInitialProps()...
        ...
       // Oh and dont forget to check if you have to call PassedComponent's getInitialProps

      // Also, only use this HOC on pages, You know why?
    }
}

@balazsorban44
Copy link
Member

This issue has been automatically locked due to no recent activity. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.

@vercel vercel locked as resolved and limited conversation to collaborators Jan 30, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants