Reputation: 1120
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
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
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