Skip to content

Suspense Like Data Fetching 🚀

Compare
Choose a tag to compare
@nimaa77 nimaa77 released this 18 Jan 21:56
· 221 commits to master since this release

Changed getInitialProps Behavior

Old Behavior

URL Change -> getInitialProps get called -> matched component get renderd on screen -> getInitialProps resolved -> component gets re-renered and can acess getInitialProps data from it's props

this is very bad and there are some problems:

  1. very bad UX, this will cause page jank since matched component gets rendered on-screen without any data, and we have to show <Spinner /> till getInitialProps resolved
  2. scroll to top happen after getInitialProps resolved, so the user will see nothing (or footer) for a while
  3. if we use data from getInitialProps with hooks, we have to write very complicated code
function PageB({ data }) 
  const [count, setCount] = React.useState(data)

  if (!data) return <Spinner />

  return <span>{count}</span>
}

PageB.getInitialProps = () => {
  return new Promise(reslove => setTimeout(() => reslove({ data: 1 }) , 3000))
}

data is undefined so count is undefined, and to fix it we have to write an effect like below:

React.useEffect(() => {
  setCount(data)
}, [ data ])

not-concurrent

New Behavior:

URL Change -> getInitialProps get called -> wait's on current location until getInitialProps resolved -> render matched component with data from getInitialProps

When a user moves from page /a to page /b, URL changes but the user still sees /a page on the screen till /b page data get fetched.

concurrent

Custom Document.js

Now we have a simpler custom Document with <AfterScripts /> and <AfterStyles />.
Any params that passed to getInitialProps and return values of it are available from __AfterContext.

import { __AfterContext } from "@jaredpalmer/after"

Use this context to build components for your custom Document.

import React from 'react';
import { AfterScripts, AfterStyles, AfterRoot, AfterData } from "@jaredpalmer/after"

class Document extends React.Component {
  static async getInitialProps({ renderPage }) {
    const page = await renderPage();

    return { ...page };
  }

  render() {
    const { helmet } = this.props;
    // get attributes from React Helmet
    const htmlAttrs = helmet.htmlAttributes.toComponent();
    const bodyAttrs = helmet.bodyAttributes.toComponent();

    return (
      <html {...htmlAttrs}>
        <head>
          <meta httpEquiv="X-UA-Compatible" content="IE=edge" />
          <meta charSet="utf-8" />
          <title>Welcome to the Afterparty</title>
          <meta name="viewport" content="width=device-width, initial-scale=1" />
          {helmet.title.toComponent()}
          {helmet.meta.toComponent()}
          {helmet.link.toComponent()}
          <AfterStyles />
        </head>
        <body {...bodyAttrs}>
          <AfterRoot />
          <AfterData />
          <AfterScripts />
        </body>
      </html>
    );
  }
}

v1.4.0...v1.5.0