Laravel in PHP made this easy with https://laravel.com/docs/9.x/session#flash-data, so I figured Next.js would have an easy way too.
I thought I'd be able to do something like:
export const getServerSideProps: GetServerSideProps = async (ctx) => {
const session = await getSession(ctx);
if (!session) {
ctx.res.setHeader("yourFlashVariable", "yourFlashValue");
console.log('headers', ctx.res.getHeaders()); // Why is it not even appearing here?
return {
redirect: {
destination: '/',
permanent: false,
},
};
}
const props = ...
return { props };
};
and then in my other page:
export const getServerSideProps: GetServerSideProps = async (context) => {
const { headers, rawHeaders } = context.req;
// look inside the headers for the variable
// ...
But the header doesn't appear.
If you know how to achieve the goal of a flash variable (even if not using headers), I'm interested in whatever approach.
(Originally I asked How can I show a toast notification when redirecting due to lack of session using Next-Auth in Next.js? but now feel like I should have asked this more generic question.)
UPDATE
I appreciate the reasonable suggestion from https://stackoverflow.com/a/72210574/470749 so have tried it.
Unfortunately, index.tsx
still does not get any value from getFlash
.
// getFlash.ts
import { Session } from 'next-session/lib/types';
export default function getFlash(session: Session) {
// If there's a flash message, transfer it to a context, then clear it.
const { flash = null } = session;
console.log({ flash });
// eslint-disable-next-line no-param-reassign
delete session.flash;
return flash;
}
// getNextSession.ts
import nextSession from 'next-session';
export default nextSession();
// foo.tsx
import { getSession } from 'next-auth/react';
import { GetServerSideProps, InferGetServerSidePropsType, NextApiRequest, NextApiResponse } from 'next';
import getNextSession from '../helpers/getNextSession';
export const getServerSideProps: GetServerSideProps = async (ctx) => {
const session = await getSession(ctx);
if (!session) {
const req = ctx.req as NextApiRequest;
const res = ctx.res as NextApiResponse;
const nSession = await getNextSession(req, res);
nSession.flash = 'You must be logged in to access this page.'; // THIS LINE CAUSES A WARNING
console.log({ nSession });
return {
redirect: {
destination: '/',
permanent: false,
},
};
}
// ...
return { props };
};
// index.tsx
import { GetServerSideProps } from 'next';
import getFlash from '../helpers/getFlash';
import getNextSession from '../helpers/getNextSession';
export const getServerSideProps: GetServerSideProps = async (context) => {
const session = await getNextSession(context.req, context.res);
let toast = getFlash(session);
console.log({ toast });
if (!toast) {
toast = 'no toast';
}
console.log({ toast });
return {
props: { toast }, // will be passed to the page component as props
};
};
Also, the nSession.flash =
line causes this warning:
warn - You should not access 'res' after getServerSideProps resolves. Read more: https://nextjs.org/docs/messages/gssp-no-mutating-res
CodePudding user response:
Your first code is working fine for me (printing the headers in terminal). However, the combination will not work as intended because the headers you set in /foo
(say) will be sent to browser, along with a status code of 307, and a location header of /
. Now "the browser" will be redirecting to the location and it won't forward your headers. Similar threads: https://stackoverflow.com/a/30683594, https://stackoverflow.com/a/12883411.
To overcome this, you can do something like this. This works because the browser sends the cookies (set while you create a session). Also note that the session
in this example is different than that of next-auth
. You can have both side by side, just change the identifiers.
// lib/useFlash.js
export const useFlash = (session) => {
// if there's a flash message, transfer
// it to a context, then clear it
const { flash = null } = session;
delete session.flash;
return flash;
};
// lib/getSession.js
import nextSession from 'next-session';
export const getSession = nextSession();
// pages/foo.jsx
import { getSession } from '../lib/getSession.js';
const Foo = () => null;
export default Foo;
export const getServerSideProps = async ({ req, res }) => {
const session = await getSession(req, res);
session.flash = { foo: 'bar' };
return { redirect: { destination: '/', permanent: false } };
};
// pages/index.jsx
import { getSession } from '../lib/getSession';
import { useFlash } from '../lib/useFlash';
const Home = ({ flash }) => (flash ? <div>{JSON.stringify(flash)}</div> : <div>Hello World</div>);
export default Home;
export const getServerSideProps = async ({ req, res }) => {
const session = await getSession(req, res);
const flash = useFlash(session);
return { props: { flash } };
};
You can modify it for TS, but the core will remain the same. The implementation is adapted from "Web Development with Node and Express: Leveraging the JavaScript Stack", Ethan Brown.