I have set a cookie with nookies which store the values of all the products selected by user. I want to fetch the cookie in server side using getServerSideProps and pass the value as props. I have to display the value of cookie on all pages.
When I tried getServerSideProps in _app.js. It did not worked and it did not even run the code.
Is there any way to do it?
As long as you don't implement getInitialProps, getStaticProps, getServerProps in _app.
If you want to render a page at the time of the request, you can use getServerSideProps to render a page on the server before responding to a request. getServerSideProps will mark the page to be rendered on each request.
getServerSideProps or API RoutesAn API route is used to fetch some data from a CMS. That API route is then called directly from getServerSideProps . This produces an additional call, reducing performance. Instead, directly import the logic used inside your API Route into getServerSideProps .
getStaticProps(): A method that tells the Next component to populate props and render into a static HTML page at build time. getServerSideProps(): A method that tells the Next component to populate the props and render into a static HTML page at run time.
In simple terms, getServerSideProps enables a page to render server-side. getServerSideProps renders your client-side page in server-side and returns a hydrated SEO-friendly HTML document to the browser. Meaning getServerSideProps pre-renders the page on each request using the data it retrieves from the server. Table of content 1.
The only way to export getServerSideProps is from a page. It is not possible to ship it from non-page files. It is important to note that getServerSideProps must be exported as a standalone function; it won't work if we make it a page component property. When should we use getServerSideProps?
By default, Next.js pre-renders every page. This means that Next.js generates HTML for each page in advance, instead of having it all done by client-side JavaScript. Pre-rendering can result in better performance and SEO. Each generated HTML is associated with minimal JavaScript code necessary for that page.
Imports used will not be bundled for the client-side. This means you can write server-side code directly in getServerSideProps, including fetching data from your database. The context parameter is an object containing the following keys:
getServerSideProps
does not work in _app.js
. see docs.
you could use the older getInitialProps
in your custom app component but then the automatic static optimisation is disabled, which is something Next.js bets on heavily.
it might be worth digging into your cookie use case and figure out if you really need to read it on the server side.
As of now, there isn't a built-in way to do it, so I've resorted to doing the following.
First, I created a file that holds the getServerSideProps
function I want to run on every page:
// lib/serverProps.js
export default async function getServerSideProps(ctx) {
// do something
return {
// data
};
}
Then in every page (yes, every, I can't find a workaround; it might even be helpful if you don't need the code to execute on server pages), do:
import getServerSideProps from "../lib/serverProps";
// other stuff...
export { getServerSideProps };
or
// other stuff...
export { default as getServerSideProps } from "../lib/serverProps";
If you want to add other code to run inside getServerSideProps
for a specific page, you could do something along the lines...
import serverProps from "../lib/serverProps";
// other stuff...
export async function getServerSideProps(ctx) {
// do custom page stuff...
return {
...await serverProps(ctx),
...{
// pretend this is what you put inside
// the return block regularly, e.g.
props: { junk: 347 }
}
};
}
For those wanting to share state received from a page's getServerSideProps
function to global components in pages/_app.tsx
, I've pieced this solution together.
getServerSideProps
function to include on all pagesuseSetUserStorage
custom hook to include on all pageslocalStorage
changes with custom event listener in global component (e.g. GlobalNav
)It's a work around, but is working for me so far (note that it includes some specifics to my use of getServerSideProps
function).
It's a fair amount of code but hopefully this helps someone:
// src/pages/_app.tsx
import type { AppProps } from "next/app";
import GlobalNav from "../components/GlobalNav";
function MyApp({ Component, pageProps: { session, ...pageProps } }: AppProps) {
return (
<>
<GlobalNav /> // <— your global component
<Component {...pageProps} />
</>
);
}
export default MyApp;
// src/utils/getServerSideProps.ts
import { ppinit, ppsession, sess } from "../authMiddleware";
import nc from "next-connect";
import { NextApiRequest, NextApiResponse } from "next";
import { User } from "../types/types";
interface ExtendedReq extends NextApiRequest {
user: User;
}
interface ServerProps {
req: ExtendedReq;
res: NextApiResponse;
}
interface ServerPropsReturn {
user?: User;
}
//
// Here we use middleware to augment the `req` with the user from passport.js
// to pass to the page
// src: https://github.com/hoangvvo/next-connect/tree/21c9c73fe3746e66033fd51e2aa01d479e267ad6#runreq-res
//
const getServerSideProps = async ({ req, res }: ServerProps) => {
// ADD YOUR CUSTOM `getServerSideProps` code here
const middleware = nc()
.use(sess, ppinit, ppsession)
.get((req: Express.Request, res: NextApiResponse, next) => {
next();
});
try {
await middleware.run(req, res);
} catch (e) {
// handle the error
}
const props: ServerPropsReturn = {};
if (req.user) props.user = req.user;
return { props };
};
export interface Props {
user?: User;
}
export default getServerSideProps;
// src/hooks.ts
import { useEffect } from "react";
import { User } from "./types/types";
export const useSetUserStorage = (user?: User) => {
useEffect(() => {
if (user) {
localStorage.setItem("user", JSON.stringify(user));
} else {
localStorage.removeItem("user");
}
// whether setting or removing the user, dispatch event so that `GlobalNav`
// component (which is above the page implementing this hook in the
// component hierarchy) can be updated to display the user status. we
// can't use `window.addEventListener('storage', handler)` as this only
// works for listening for events from other pages
document.dispatchEvent(new Event("localStorageUserUpdated"));
});
return null;
};
// src/pages/index.tsx (or any page)
import { useSetUserStorage } from "../hooks";
import { Props } from "../utils/getServerSideProps";
export { default as getServerSideProps } from "../utils/getServerSideProps";
export default function Home({ user }: Props) {
useSetUserStorage(user);
return (
<>
<h1>Welcome to my app {user?.username}</h1>
</>
);
}
// src/components/GlobalNav.ts (or another global component)
import { useEffect, useState, MouseEvent } from "react";
import { User } from "../types/types";
const GlobalNav = () => {
const [user, setUser] = useState<User | null>(null);
useEffect(() => {
const handleUserLocalStorage = () => {
const userString = localStorage.getItem("user");
try {
if (userString) {
setUser(JSON.parse(userString));
} else {
setUser(null);
}
} catch (e) {
// handle parse error
}
};
handleUserLocalStorage();
// this component (`GlobalNav`) lives at the application level, above the
// pages, but the pages receive the user object from `getServerSideProps`,
// so this listener listens for when a page tells us the user object has
// changed so we can update the `user` state here.
document.addEventListener(
"localStorageUserUpdated",
handleUserLocalStorage,
false,
);
return () => {
// remove listener if component unmounts
document.removeEventListener(
"localStorageUserUpdated",
handleUserLocalStorage,
);
};
}, []);
return (
<div>
{user?.username}
</div>
);
};
export default GlobalNav;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With