tunesmith
tunesmith

Reputation: 1120

NextJS: Is there a way to invoke something only on serverside, only on a fresh page load?

Our team initially created an _app.tsx with getInitialProps, and included something along the lines of

if (!req) return {};

as the top line. The idea was to make getInitialProps run only on the server side, only on initial page load. We thought it was a safe assumption as the usual advice (often repeated in stackoverflow answers) is that getInitialProps runs client-side on page transitions.

We later discovered that getInitialProps actually runs server-side on page transitions, if you are transitioning to a page that has getServerSideProps.

So that doesn't serve our purposes - we have several network queries that are made in getInitialProps that are being made on most of our page transitions (since most of our pages have getServerSideProps), when our intention was to only run those queries on a fresh page (app) load.

We could instead use useEffect only on component mount, but we want to run those queries serverside instead of client side, to protect against visual flashes.

I'm learning how to set up a context, with a setState inside, but initial testing shows that its initial setState runs on both clientside and serverside:

...

const getString = (): string => {
  console.log('about to set string!');
  return 'string';
};

const PagePropsProvider: React.FC<IProps> = ({ pageProps, children }) => {
  const [sample, setSample] = useState<string>(getString());

  return <PagePropsContext.Provider value={pageProps}>{children}</PagePropsContext.Provider>;
};
...

What I'd like, for something like an auth context, is for _app.tsx's getInitialProps to request the initial user object server side on page load, supply it to the context from the props, and not have the app re-request the initial user object unnecessarily on every page transition. I could do that if I could prevent getInitialProps from running on page transitions. Is there a way to prevent getInitialProps from running on those page transitions? Or a more idiomatic way to do what we're aiming for? (Ideally we'd prefer to avoid getInitialProps entirely, but the same issue remains with getServerSideProps, how to make it invoke only on a fresh page load.)

Upvotes: 5

Views: 5594

Answers (2)

tunesmith
tunesmith

Reputation: 1120

By closely examining the context object in both getInitialProps and getServerSideProps, I've noticed one element that appears to reliably indicate the difference between a client-side page transition and a fresh page load: context.req.url always seems to start with /_next/data for a client-side page transition - even if the code itself (getInitialProps or getServerSideProps) is being run on the server side.

So we are trying out code that looks like:

if (!req || (req.url && req.url.startsWith('/_next/data'))) {
  // return early
}
...

I'm not certain if this is reliable or "supported" for future releases, so I've also opened a feature request here:

https://github.com/vercel/next.js/discussions/19459

Upvotes: 6

Arthur
Arthur

Reputation: 23

getInitialProps is only called server-side on the first load. During page transitions, getInitialProps in only called browser-side, whereas getServerSideProps will be called server-side on first load and during all transitions.

If you only want to fetch data server-side during the first-load, you should use getInitialProps only, like this:

MyPage.getInitialProps = async () => {
  if (typeof window !== 'undefined') {
    return {} // browser-side, fetch data directly in component
  }

  console.log('server-side !');
  const data = {} // fetch your data
  return { data }
};

You can then set your context state with the received props.

const PagePropsProvider: React.FC<IProps> = ({ pageProps, children }) => {
  const [state, setState] = useState()
  useEffect(() => {
    if (pageProps.data) {
      setState(pageProps.data)
    }
  }, [pageProps.data])
  return <PagePropsContext.Provider value={state}>{children}</PagePropsContext.Provider>;
};

Upvotes: 2

Related Questions