Execute on every page
Answered
Silver posted this in #help-forum
SilverOP
So i am trying to have a verifySession() function run on every page/navigation.
Layout.jsx
to see when the function is executed i have a console.log
However when using the Link from 'next/link' the pages are cached (or whatever its called) so the function is only executed the first time.
what would be the possible solution?
Layout.jsx
import DashboardWrapper from '@/app/components/DashboardWrapper'
import { verifySession } from '@/app/components/session'
export default async function RootLayout({ children }) {
await verifySession()
return (
<DashboardWrapper>{children}</DashboardWrapper>
)
}to see when the function is executed i have a console.log
export async function verifySession() {
console.log('executed')
}However when using the Link from 'next/link' the pages are cached (or whatever its called) so the function is only executed the first time.
what would be the possible solution?
Answered by Silver
here is the solution i will utilize since the point is to ensure user is auth before accessing data. using NextJS with MUI for loading animation @Ray
template.jsx
template.jsx
'use client'
import { useEffect, useState } from 'react'
import { verifySession } from '@/app/components/session'
export default function DashbaordTemplate({ children }) {
let [isAuth, setIsAuth] = useState(false)
useEffect(() => {
(async function () {
console.log('Template.jsx executed')
let auth = await verifySession()
setIsAuth(auth)
if (!auth) {
location = '/logout'
}
})()
}, [])
if (!isAuth) {
return null
}
return <>{children}</>
}165 Replies
You can use a middleware to check the session @Silver
@B33fb0n3 You can use a middleware to check the session <@679398056679964693>
SilverOP
cant since it is checking the database and prisma does not support it natively
Ah verifySession comes from prisma?
Black Caiman
Did you try adding export const dynamic = 'force-dynamic';?
@B33fb0n3 Ah verifySession comes from prisma?
SilverOP
no? it is a function that uses prisma
@Black Caiman Did you try adding export const dynamic = 'force-dynamic';?
SilverOP
yes. makes no difference
However when using the Link from 'next/link' the pages are cached (or whatever its called) so the function is only executed the first time.this sounds like client page caching ngl... you can stop this with using
a instead of Link... but it only lasts 30sec iirc@riský > However when using the Link from 'next/link' the pages are cached (or whatever its called) so the function is only executed the first time.
this sounds like client page caching ngl... you can stop this with using `a` instead of `Link`... but it only lasts 30sec iirc
SilverOP
no other work around @riský ? the Link functionality is nice
i mean you can invalidate it with router.refresh iirc
i don't know many ways of deleting the cache/not using it
@riský i mean you can invalidate it with router.refresh iirc
SilverOP
can you explain router.refresh?
https://nextjs.org/docs/app/api-reference/functions/use-router (and scoll a little untill you see refresh in list)
@riský https://nextjs.org/docs/app/api-reference/functions/use-router (and scoll a little untill you see refresh in list)
SilverOP
doest work/solve the problem
but if you load page and wait a min and then click link, does it achieve what you want?
SilverOP
never mind so it does work. however
router.refresh() just keeps executing until the application crashestry putting it in a single useEffect with no deps (just empty list like nomal)
but i can actually see why it would infinite load
and my solution isn't even that good as you have 2 requs...
best thing is to actually disable
prefetcing on the linkSilverOP
i got a good solution!
let [currentPathname, setCurrentPathname] = useState(pathname)
let pathname = usePathname()
let router = useRouter()
useEffect(() => {
if (currentPathname !== pathname) {
setCurrentPathname(pathname)
router.refresh()
}
}, [currentPathname, pathname])only renders once
niceee
can you try disableing prefetching on the links also tho
i think its a way better method....
@riský can you try disableing prefetching on the links also tho
SilverOP
<Link prefetch={false}/> didnt work for me previouslyoh
i think i missunderstood you then
ahh actually i remember what you wanted now
yeah i think the refresh might be best ðŸ˜
SilverOP
yes and it seems to work perfectly and does not seem to affect performance as it still has the prefetch and memory cache
@riský oh
SilverOP
thank you for the assistance!
also can i ask why you actually need to do the verify every page?
as is first time not good enough?
@riský also can i ask why you actually need to do the verify every page?
SilverOP
authentication. verifying session is still valid. since a user session can be revoked or invalidated
hmm i see, and you don't want stale data too
@riský hmm i see, and you don't want stale data too
SilverOP
exactly! stale data can be a security issue too if not handle correctly.
i haven't tried it, but maybe
experimental.optimisticClientCache in next config could bypass the cache... https://github.com/vercel/next.js/blob/canary/packages/next/src/client/link.tsx#L141only try if you have time tho
hmm this may not actully work ater looking further at it, so disregard it
isnt the RootLayout only render once? so you need to do router.refresh to make it render again?
lol you have a really good point
how did i miss that ðŸ˜
so its not related to cache lol
i mean he just wants no cache ig
which is why just use
a link...ah ok
@Ray isnt the RootLayout only render once? so you need to do router.refresh to make it render again?
can be simplified to any layout 🙂
@Ray ah ok
its bad calling it cache here (and i hope you get what i mean), and you have a really good point (thanks for telling me)
i think maybe template is better then?
@riský i think maybe template is better then?
yea worth a try
@Ray isnt the RootLayout only render once? so you need to do router.refresh to make it render again?
SilverOP
same issue even if it is located in the individual pages. being in the RootLayout is not the issue
@Ray because router cache
SilverOP
yup. so
router.refresh() with my other code is the solutonhowever, that would hurt the performance i guess
middleware is best place for that, I think you can create a route handler to check the session with prisma and you can fetch the api in middleware
@Silver yes and it seems to work perfectly and does not seem to affect performance as it still has the prefetch and memory cache
SilverOP
dont see any performance difference at the moment
@Ray middleware is best place for that, I think you can create a route handler to check the session with prisma and you can fetch the api in middleware
SilverOP
prisma does not work in middleware natively
and fetch the api in middleware
SilverOP
oh i get what you mean
i will test it out in a bit and see
export async function middleware(req: NextRequest) {
await fetch(new URL("/api/session", req.nextUrl));
}@Ray ts
export async function middleware(req: NextRequest) {
await fetch(new URL("/api/session", req.nextUrl));
}
SilverOP
middleware doesnt appear to execute every time either
oh wait
it was caching. hold on
nope nevermind. the middleware itself seems to get cached. cant see a workaround to that @Ray
based on https://github.com/vercel/next.js/discussions/43675 people are using
route.refresh() to solve the middleware cache anyways...@Silver based on https://github.com/vercel/next.js/discussions/43675 people are using `route.refresh()` to solve the middleware cache anyways...
yea there is still a 30 sec router cache with <Link prefetch={false} />
// template.tsx
"use client";
import { useEffect } from "react";
import { verify } from "./action";
export default function Template({ children }: { children: React.ReactNode }) {
useEffect(() => {
verify();
}, []);
return <div>{children}</div>;
}this works
@Ray yea there is still a 30 sec router cache with <Link prefetch={false} />
SilverOP
its odd because it doesnt always cache the middleware...
@Ray ts
// template.tsx
"use client";
import { useEffect } from "react";
import { verify } from "./action";
export default function Template({ children }: { children: React.ReactNode }) {
useEffect(() => {
verify();
}, []);
return <div>{children}</div>;
}
this works
SilverOP
wouldnt work. since it is a server function that access header cookies
im using server action
it works, i just tried
@Ray it works, i just tried
SilverOP
ReactServerComponentsError:
You're importing a component that needs next/headers. That only works in a Server Component but one of its parents is marked with "use client", so it's a Client Component.
Learn more: https://nextjs.org/docs/getting-started/react-essentialsyou cannot access cookies in client component
no im using server action
@Ray ts
// template.tsx
"use client";
import { useEffect } from "react";
import { verify } from "./action";
export default function Template({ children }: { children: React.ReactNode }) {
useEffect(() => {
verify();
}, []);
return <div>{children}</div>;
}
this works
SilverOP
you are using
verify() in a client compoent.....@Ray client component can execute server action
SilverOP
ah forget about
'use server'no
no cache at all
it is a
template.tsxSilverOP
oh give me a sec
@Ray it is a `template.tsx`
SilverOP
got it to work. only issue now is that it is executing twice
because react strict mode
it will run once in production mode
@Ray it will run once in production mode
SilverOP
about to check build
now when bulding i get the following error
note my verify function is async since it is checking the database
[Error]: Dynamic server usage: Page couldn't be rendered statically because it used `cookies`. See more info here: https://nextjs.org/docs/messages/dynamic-server-errornote my verify function is async since it is checking the database
search for cookies and see what page you are importing it
this is not related to the template i think, you can rename it to _template.tsx and try building again
@Ray this is not related to the template i think, you can rename it to _template.tsx and try building again
SilverOP
you are right. just some weird code that decided to break. unrelated
@Ray ts
// template.tsx
"use client";
import { useEffect } from "react";
import { verify } from "./action";
export default function Template({ children }: { children: React.ReactNode }) {
useEffect(() => {
verify();
}, []);
return <div>{children}</div>;
}
this works
SilverOP
this solution works however what i noticed is that it now makes an additional http request caused by the useEffect
yea because of server action
SilverOP
so realistically doesnt it have the same amount of effect on the performance as
router.refresh()?@Silver so realistically doesnt it have the same amount of effect on the performance as `router.refresh()`?
router refresh will full reload your page from layout to other component. While server action just will make a extra http request on page load.
you should try with yourself which suit your usecase better
you should try with yourself which suit your usecase better
i don't know how your application load the data
@Ray router refresh will full reload your page from layout to other component. While server action just will make a extra http request on page load.
you should try with yourself which suit your usecase better
SilverOP
does the additional http request that is caused by useEffect refetch the whole page?
no
the page will still render while still posting the server action.
try add a timeout on the server action, the page will still render and not awaiting it
try add a timeout on the server action, the page will still render and not awaiting it
"use server";
import { redirect } from "next/navigation";
export async function verify() {
await new Promise((resolve) => setTimeout(resolve, 5000));
console.log("verfiy");
redirect("/");
}like this
SilverOP
here is the solution i will utilize since the point is to ensure user is auth before accessing data. using NextJS with MUI for loading animation @Ray
template.jsx
template.jsx
'use client'
import { useEffect, useState } from 'react'
import { verifySession } from '@/app/components/session'
export default function DashbaordTemplate({ children }) {
let [isAuth, setIsAuth] = useState(false)
useEffect(() => {
(async function () {
console.log('Template.jsx executed')
let auth = await verifySession()
setIsAuth(auth)
if (!auth) {
location = '/logout'
}
})()
}, [])
if (!isAuth) {
return null
}
return <>{children}</>
}Answer
SilverOP
@Ray ever seen the template.jsx not be executed? from what i am seeing if i am visiting the same parent path it is not execute template.jsx multiple times. e.g. domain.com/website, domain.com/website/view, domain.com/website/edit will only execute template.jsx once. but if i switch between domain.com/website and domain.com/dashboard it executes every time
@Silver <@743561772069421169> ever seen the template.jsx not be executed? from what i am seeing if i am visiting the same parent path it is not execute template.jsx multiple times. e.g. domain.com/website, domain.com/website/view, domain.com/website/edit will only execute template.jsx once. but if i switch between domain.com/website and domain.com/dashboard it executes every time
try create a
template.tsx in app/website/template.tsx?@Ray try create a `template.tsx` in app/website/template.tsx?
SilverOP
but then it will double execute the code no? since app/website/template.jsx will be nested in app/template.jsx
SilverOP
@Ray any suggestions?
@Silver but then it will double execute the code no? since app/website/template.jsx will be nested in app/template.jsx
templates create a new instance for each of their children on navigation.
@Ray try create a `template.tsx` in app/website/template.tsx?
so this should do what you need
or create a client component and wrap the page you need to run instead of using template
SilverOP
what i mean is wont localhost:3000/website/view execute /app/website/template.jsx and /app/template.jsx meaning template.jsx will run twice? @Ray
btw, if you need something to run on every page navigation (like auth) this should happen in
https://nextjs.org/docs/app/building-your-application/routing/middleware
middleware which you can keep to matching pathshttps://nextjs.org/docs/app/building-your-application/routing/middleware
@Marchy btw, if you need something to run on every page navigation (like auth) this should happen in `middleware` which you can keep to matching paths
https://nextjs.org/docs/app/building-your-application/routing/middleware
SilverOP
middleware is out of the questions. i believe i mentioned it in previous message
@Silver what i mean is wont localhost:3000/website/view execute /app/website/template.jsx and /app/template.jsx meaning template.jsx will run twice? <@743561772069421169>
does it run twice? Im not sure about it. if it run twice, maybe create a client component and wrap the page you need instead of using template
@Ray does it run twice? Im not sure about it. if it run twice, maybe create a client component and wrap the page you need instead of using template
SilverOP
That just goes back to the main issue... since a component is not executed on every navigation
@Silver That just goes back to the main issue... since a component is not executed on every navigation
a client compnoent with useEffect inside
just like the code inside template
@Ray just like the code inside template
SilverOP
already tested on my end. using a component only executes once.
the useEffect only execute once ?
@Ray the useEffect only execute once ?
SilverOP
yes because of cache i suppose
i dont think so
it is just a client side fetching
@Ray it is just a client side fetching
SilverOP
please test it and you will see it does not execute every time
already tested and it execute every time
@Ray already tested and it execute every time
SilverOP
can you provide your code then...
same code in template
@Ray same code in template
SilverOP
i used that exact code and it did not run every time...
@Ray same code in template
SilverOP
please show me how you created the component 🙂
"use client";
import { verify } from "@/app/action";
import { useEffect, useState, useTransition } from "react";
export default function Every({ children }: { children: React.ReactNode }) {
const [show, setShow] = useState(false);
useEffect(() => {
(async function () {
await verify();
setShow(true);
})();
}, []);
if (!show) {
return <div>loading...</div>;
}
return <div>{children}</div>;
}import Every from "@/components/every";
export default function Page() {
return (
<>
<Every>1</Every>
</>
);
}@Ray ts
"use client";
import { verify } from "@/app/action";
import { useEffect, useState, useTransition } from "react";
export default function Every({ children }: { children: React.ReactNode }) {
const [show, setShow] = useState(false);
useEffect(() => {
(async function () {
await verify();
setShow(true);
})();
}, []);
if (!show) {
return <div>loading...</div>;
}
return <div>{children}</div>;
}
SilverOP
please add console.log('executed') to the async function and you will see it is not running every time. why was the useTransition imported?
it is
I see
loading... every page changed@Ray I see `loading...` every page changed
SilverOP
that is separate. please add the console.log('executed') to the async function and you will see it is not running every time
have you ever tried the code? or just asking?
@Ray have you ever tried the code? or just asking?
SilverOP
i am using the code right now. and it does not execute every time for me
that is why i am confused
can you show your code
and do you see
loading... on page changea post request is made on navigation
SilverOP
huh i found the issue... and it is so dumb lol. so if i add the AuthWrapper.jsx directly to my DashboardWrapper.jsx instead of the individual page.jsx then it does not execute every time. I was trying to prevent having to redefine the AuthWrapper. So i guess if you add a component inside another component it is cache. that is annoying... i wonder if there is a solution @Ray
all you need is wrap the page component
it can't be cache just a client side fetching inside useEffect
but you have to wrap every page which was template doing for you
@Ray but you have to wrap every page which was template doing for you
SilverOP
I guess so. thanks for the assist
@Ray yea there is still a 30 sec router cache with <Link prefetch={false} />
Audubon's Oriole
I don't think there is 30sec cache for Link tag. It is refetching data when user clicks on Link tag
Audubon's Oriole
so Link & router.push has same cache 30sec right?
yes 30sec for dynamic page and 5min for static page
Audubon's Oriole
so what is way to get fresh data?
Audubon's Oriole
I am revalidating data but when I go to that page using router.push then only I get new data. Browser back/forward button not giving me fresh data
how do you revalidate the data
I think you should create a thread
@Ray yes 30sec for dynamic page and 5min for static page
Audubon's Oriole
yes but when I click on Link tag its calling my server function. I am getting consoles
@Ray how do you revalidate the data
Audubon's Oriole
using revalidatetag/revalidatepath
@Audubon's Oriole yes but when I click on Link tag its calling my server function. I am getting consoles
how do you click a Link tag to call a function?
please create a thread and show more detail
Audubon's Oriole
okay
Audubon's Oriole
@Ray I have created thred. Please look into my scenario
I have list of stocks on homepage. When I click on any stocks its taking user's to its details page. Details page data are dynamic and changes every seconds. Problem is when i redirect to stock details page its showing me stale data rather than showing me fresh as I have used export const dynamic = 'force-dynamic'
I have list of stocks on homepage. When I click on any stocks its taking user's to its details page. Details page data are dynamic and changes every seconds. Problem is when i redirect to stock details page its showing me stale data rather than showing me fresh as I have used export const dynamic = 'force-dynamic'