loading...

How to reload a Next.js page's initial props without reloading the whole page

robbiegm profile image RobbieGM ・2 min read

If you have made a Next.js page that loads data in its getInitialProps function, you may have wanted to reload it in order to fetch the newest data after making some mutating API call. Hard-reloading the page will reset the user's scroll position and take extra time, so clearly that is not the best solution. You could also make some fetchData function that gets called in getInitialProps, passes its data into the default value of a useState hook, and then gets updated by calling fetchData again followed by setting the state. But wouldn't it be convenient if you could just have a function that reloaded your initial props?
I made a higher order component to wrap Next.js pages that does exactly this. Here it is in TypeScript:

import { NextPage, NextPageContext } from 'next';
import React, { useState, PropsWithChildren, ComponentType } from 'react';

export interface SoftReloadProps {
  /**
   * Reloads the page's initialProps without causing a real browser "hard" reload.
   */
  softReload(): void;
}

type OmitSoftReloadProps<T> = Omit<T, keyof SoftReloadProps>;
type SoftReloadablePageInitialProps<TProps> = OmitSoftReloadProps<TProps> & { context: NextPageContext };
export type NextPageWithInitialProps<P, IP = P> = NextPage<P, IP> & Required<Pick<NextPage<P, IP>, 'getInitialProps'>>;

/**
 * Removes never-used context values to reduce bloat. Context values may come from server but then
 * be used client-side because they are saved in initial props.
 */
function minifyContext(context: NextPageContext): NextPageContext {
  return { ...context, req: undefined, res: undefined };
}

const withSoftReload = <TProps extends SoftReloadProps>(
  Page: NextPageWithInitialProps<TProps, OmitSoftReloadProps<TProps>>
): NextPage<SoftReloadablePageInitialProps<TProps>> => {
  async function getInitialProps(ctx: NextPageContext): Promise<SoftReloadablePageInitialProps<TProps>> {
    return { context: minifyContext(ctx), ...(await Page.getInitialProps(ctx)) };
  }
  const omitContextFromProps = ({
    context,
    ...props
  }: SoftReloadablePageInitialProps<TProps>): OmitSoftReloadProps<TProps> => props as any;
  const NewPage: NextPageWithInitialProps<SoftReloadablePageInitialProps<TProps>> = props => {
    // set inner page initial props to wrapper initial props minus context
    const [initialProps, setInitialProps] = useState(omitContextFromProps(props));
    async function softReload() {
      setInitialProps({ children: null, ...(await Page.getInitialProps(props.context)) });
    }
    return (
      <Page
        {...(({ ...initialProps, softReload } as Omit<TProps, keyof SoftReloadProps> & SoftReloadProps) as TProps)}
      />
    );
  };
  NewPage.getInitialProps = getInitialProps;
  NewPage.displayName = `withSoftReload(${Page.displayName})`;
  return NewPage;
};

export default withSoftReload;

You can use this HOC like this in your pages:

interface InitialProps {
  data: string;
}

const MyPage: NextPageWithInitialProps<InitialProps & SoftReloadProps, InitialProps> = ({ data, softReload }) => (
  <div>
    {data}
    <button onClick={softReload}>Refresh</button>
  </div>
);

MyPage.getInitialProps = async (ctx) => {
  // fetch data
};

export default withSoftReload(MyPage);

Hope you enjoy!

Posted on May 18 by:

robbiegm profile

RobbieGM

@robbiegm

I'm a web developer who loves to work with Node, Express, TypeScript, GraphQL, and React. I also have experience with Python, Java, Redux, and Heroku.

Discussion

markdown guide
 

From the NextJS documentation:

If you're using Next.js 9.3 or newer, we recommend that you use getStaticProps or getServerSideProps instead of getInitialProps.

These new data fetching methods allow you to have a granular choice between static generation and server-side rendering.

 

I've considered using getServerSideProps but then it wouldn't work client side. I use isomorphic fetching in getInitialProps, what is the new recommended way to do that?

 

I highly recommend that you listen to this:

fullstackradio.com/137

"Why Zeit as a company has started to favor client-side data fetching with SWR over getInitialProps/getServerSideProps, and how they are combining that with statically pregenerated “shells” for incredibly fast feeling experiences"

 

How to implement this on Hugo?