Next.js: [RFC] Exported `getInitialProps`

Created on 27 Aug 2019  路  9Comments  路  Source: vercel/next.js

Goal

Make using getInitialProps easier for function components.

Background

When static method getInitialProps was added the main way to create React components where classes, an example of how a Page level component looks with this is:

export default class MyPage extends React.Component {
  static async getInitialProps(ctx) {
    // do something here
  }

  componentDidMount() {
    // run some effect
  }

  render() {
    // render something here
  }
}

With Hooks page levels components with complex logic could be created a simpler function components.

export default function MyPage(props) {
  React.useEffect(() => {
    // run some effect
  });

  // render something here
}

But if we want to use getInitialProps we still need to ues static method, that means doing something like this:

MyPage.getInitialProps = async ctx => {
  // do something here
}

While it works I have found it a little repetitive and not the best experience I would expect from Next.js and as it was it class based components.

Proposal

I propose add support to do a named export for getInitialProps, the function component file will then look like this:

export default function MyPage(props) {
  React.useEffect(() => {
    // run some effect
  });

  // render something here
}

export async function getInitialProps(ctx) {
  // do something here
}

We could still support the static method which for classes based components.

This will also match how the config is exported for API endpoints.

As a side effect of this I could re-export getInitialProps from another module with a single line.

export default function MyPage(props) {
  React.useEffect(() => {
    // run some effect
  });

  // render something here
}

export { getInitialProps } from "../another/module

This could let me extract it to another file and share it easier.

Most helpful comment

Closing as the new exported getServerSideProps and getStaticProps use this behavior. Thanks for initial thoughts!

All 9 comments

This is something @Timer has been wanting to do for a while, however I'm still not convinced it's the way to go. It depends on how data fetching ends up in the near future.

For what it's worth, this does get a reduction in bundle size as the getInitialProps Identifier no longer has to be preserved in the bundle, but it's really negligible.

EDIT: unfortunately it'd still be needed as its presence needs to be detected at runtime 馃槥

1. HOCs & Composition

Many HOCs out there use and extend the getInitialProps method.

https://github.com/zeit/next.js/blob/73f6e04b6bfe02c95eeffd7d113eedcb82da9300/examples/with-apollo/lib/apollo.js#L44-L45

How would such a pattern look like using your proposal?

2. Anonymous functions

In the past I've seen many people exporting an anonymous function as default export from the pages directory. I believe this is a bad pattern since it will decrease debugability when using devtools or tools like sentry. The current implementation enforces people to name their PageComponents. I believe thats a good thing.

3. File extraction

Currently you can extract your getInitialProps method like follows:

import getInitialProps from '...'

const MyPage = () => (
   ...
)

MyPage.getInitialProps = getInitialProps
  1. HOCs wouldn鈥檛 need to do something special for getInitialProps anymore since the method will be exported separately of the component, if you want to extend the gIP method you could create a High Order Function and compose it with the exported one. This will let devs only compose what they need.

  2. This doesn鈥檛 change, you could use an arrow function, assign it to a constant, apply getInitialProps and export it. If people want to do it they will, also is not a Next issue but more something React docs should warn about.

  3. Yeah, but with exported gIP you can just export directly from the import, in a single line. Not a big deal however.

The proposal does not play well with TypeScript, as the expected return type of getInitialProps cannot be automatically inferred, whereas currently it can when using the MyPage.getInitialProps = idiom.

@fabb
Isn't MyPage.getInitialProps only typed when the user manually annotates his component with the NextPage type?

If so, it means the user already had to explicitly annotate something for the MyPage.getInitialProps type to work as expected, so what's another type annotation?
In fact, if we'd type the getInitialProps export _separately_, we won't need NextPage anymore (unless I'm forgetting something), so we're left with only 1 type, which is more concise (and less dependant on react's types?).
Hopefully I'm not missing anything.

You need to type it, otherwise you cannot typesafely access the props in render.

Whoops, that's what I missed.
So it'll mean manually typing 2 things instead of 1 for the same result. I see why this is less than ideal.

Closing as the new exported getServerSideProps and getStaticProps use this behavior. Thanks for initial thoughts!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

flybayer picture flybayer  路  3Comments

olifante picture olifante  路  3Comments

ghost picture ghost  路  3Comments

knipferrc picture knipferrc  路  3Comments

rauchg picture rauchg  路  3Comments