How not to show stale cache, but loading state and fresh data instead?
Unanswered
New Guinea Freshwater Crocodile posted this in #help-forum
New Guinea Freshwater CrocodileOP
I am loading and showing external images on a page. The image urls expire after a certain time. Using revalidate, I can update the cache, but the stale cache that is shown has expired image urls. They only start working again after a refresh.
Is there a way to disable the display of the stale cache and show the loading state instead, if the page has to be revalidated?
Is there a way to disable the display of the stale cache and show the loading state instead, if the page has to be revalidated?
129 Replies
New Guinea Freshwater CrocodileOP
Basically the gist of it is, that there is currently no way to do a syncronous rerender of a page and display a loading state, if it is cached on the server. If the cache is stale, the rerendering is done in the background and the cache is served.
I don't know if there is a streaming update when you are already on the page and just use a <Link /> to navigate to the stale page. But if you hit the stale page directly, there is no update of the data.
But even if there was a streaming update, there should be an optino not to show the stale cache but the loading state again. In some cases it might be beneficial to show loading instead of the outdated page.
I would love some more input on this topic.
I don't know if there is a streaming update when you are already on the page and just use a <Link /> to navigate to the stale page. But if you hit the stale page directly, there is no update of the data.
But even if there was a streaming update, there should be an optino not to show the stale cache but the loading state again. In some cases it might be beneficial to show loading instead of the outdated page.
I would love some more input on this topic.
I'm facing the same issue and I just resided to live with it. However this is not a very preferable solution and I'd love if there was some way of forcing the (router?) to display fresh data.
New Guinea Freshwater CrocodileOP
a cron job is a workaround to make sure the page is always up to date for the user if you have a known revalidation interval. but it feels very dirty, hovever better than having the page rendered on every request. I feel like the caching needs some more love.
That works for the server cache only though - your problem seems to be client side
so basically you would also need to tell the router to use the new data on back nav
It is dirty.. Maybe alongside the
export const revalidate = N
there would also be export const revalidateOnRequest = bool
that would change the refresh strategy? I'm not completely sure of the internals however it's a huge pain point for me at the moment, and unfortunately I'm considering switching to anything else :/Feels like the new app router is designed to fit a specific niche. Too much problems from my end compared to NextJS <13
New Guinea Freshwater CrocodileOP
exactly what i was thinking, an opt in to do the rerender syncronously and not in the background
i have just started with 13, vue guy here, using the pages directory, could we easily create the needed behavior?
It's been some years (before On Demand Revalidation!), so my memory might be wrong but I think yes you could...
I'm relooking the documentation and:
I'm relooking the documentation and:
When a request is made to a page that was pre-rendered at build time, it will initially show the cached page.
Any requests to the page after the initial request and before 10 seconds are also cached and instantaneous.
After the 10-second window, the next request will still show the cached (stale) page
Next.js triggers a regeneration of the page in the background.
Once the page generates successfully, Next.js will invalidate the cache and show the updated page. If the background regeneration fails, the old page would still be unaltered.
New Guinea Freshwater CrocodileOP
Once the page generates successfully, Next.js will invalidate the cache and show the updated page. If the background regeneration fails, the old page would still be unaltered.
Initially i understood that the new content is streamed after regeneration, but that is not the case afaik. so the user that triggered the regeneration does not receive the new content
Initially i understood that the new content is streamed after regeneration, but that is not the case afaik. so the user that triggered the regeneration does not receive the new content
"the old page would still be unaltered" just means the cache is not updated on the server i guess
Well I understand the same too !
Just checked, it's copy pasted into the app router documentation as well.
Just checked, it's copy pasted into the app router documentation as well.
Either I don't remember correctly and was never working this way, or something internally changed in app router that (broke?) it?
New Guinea Freshwater CrocodileOP
so yeah, sadly this is not suitable for use cases where you do not want to show stale content but also want to use caching
to get this working the page would need to be declared dynamic and ssr on every request, which might be problematic for the api calls
all comes back to the opt in into revalidatin/rerendering when requesting the page
but i am so grateful to have found somebody with the same issue, had a long discussion last night in #discussions about this and could not get my point across XD
I'm also having another cache related issue (https://nextjs-forum.com/post/1121336097104986212)
I literally just wanted to do a simple dashboard, and in every function call that i write I have some kind of problem 😛
At this point deploying a PHP website with htmx might be less time consuming
New Guinea Freshwater CrocodileOP
Yeah, it is these little gotchas that break development for now. I am sure it will be addressed soon. check this out: https://github.com/vercel/next.js/issues/42991 i think that is the same problem you are having
Damn november 16 2022!
This is so breaking, especially when doing pagination (one of the most basic features)
Best case scenario, you forget server side stuff and you just do an api route with client side fetching
New Guinea Freshwater CrocodileOP
and that is actually the part that made me try next in the first place, because i think it can be great once ironed out
New Guinea Freshwater CrocodileOP
maybe give astro a go if it is a thing you are building from scratch and not too big, you can use all your react code just without the caching issues i think. and then migrate back to next once the issue is gone.
but then again, if auth etc is involved, astro is not the way to go
nextjs revalidation triggers when you refresh the page or do a hard navigation. you can't have it revalidate real time without using client components
New Guinea Freshwater CrocodileOP
i am not looking for real time revalidation, i just want the regeneration to run in the foreground when i visit a page. meaning that i get the loading state until the fresh content rolls in and not a stale cache. once i am on the page, no refreshing should happen, it is just about the first visit to the page
Example:
# What happens now :
Website has currently cache A loaded.
1. User visits site -> get served A
2. Time has passed and revalidation needs to happen at the server on the next request
3. User visits site -> gets instantly served A, revalidation background job starts to create cache B with new data
4. Revalidation background job ends and creates cache B with new data for the next request
# What we are asking for:
Website has currently cache A loaded.
1. User visits site -> get served A
2. Time has passed and revalidation needs to happen at the server on the next request
3. User visits site -> revalidation background job starts to create cache B with new data... User gets shown "loading.js" until this job is complete and is shown cache B
# What happens now :
Website has currently cache A loaded.
1. User visits site -> get served A
2. Time has passed and revalidation needs to happen at the server on the next request
3. User visits site -> gets instantly served A, revalidation background job starts to create cache B with new data
4. Revalidation background job ends and creates cache B with new data for the next request
# What we are asking for:
Website has currently cache A loaded.
1. User visits site -> get served A
2. Time has passed and revalidation needs to happen at the server on the next request
3. User visits site -> revalidation background job starts to create cache B with new data... User gets shown "loading.js" until this job is complete and is shown cache B
I'm currently at work, I'll see if I can make a reproduceable example by the end of the day.
However it isn't anything too fancy. Just a async page with a revalidate on it
However it isn't anything too fancy. Just a async page with a revalidate on it
https://nextjs-demos-git-dynamic-routes-alfonsusac.vercel.app/apple such as this, everytime i refresh, the loading.ts is always shown
although i intentionally waited for 2 seconds
I would link my live website, but I don't know if there's any rule of rejecting promotes etc
But in my live website I have a last update field
I just opened the website (12:29 GMT)
The website shows: Last Update: Thu, 22 Jun 2023 12:26:54 GMT
My revalidation time is 30seconds
Seems like its launching the "revalidation" job in the background and I got served an old cache
New Guinea Freshwater CrocodileOP
I dont think it is promotion if you post it here as it would help to display the case
I am not an mod though 🙂
Well if many people join, we wouldn't see the bug ahahahhaha
The more people do requests, the less obvious it is
New Guinea Freshwater CrocodileOP
I can whip up an example on vercel
give me a few mins
New Guinea Freshwater CrocodileOP
the getDate function could be async, does not change anything, behavior is the same, that is why i opted for the very simple demonstration here. breaks if we all refresh at the same time, but i think we will not do this haha
gist is, usually you will have to refresh twice to update the time
Can confirm I['m also doing the same thing, just with an API call inside the
getDate()
Wait, could it be of the missing
loading.tsx
?I don't remember if mine had it, because I did many iterations to try to find the problem
Maybe NextJS doesn't find a
loading.tsx
and is like, eh I'm gonna serve you some old data i have, because i have nothing else to show youinteresting...
New Guinea Freshwater CrocodileOP
no should not be the missing loading, i will add one
is updated, with loading
but you will never see the loading, as it uses the cache from build time first, and then the cache created by our relentless refreshing
tim?
New Guinea Freshwater CrocodileOP
i have also tried using the cache function, but that does not have a second argument like fetch to revalidate the request
nextjs doesn't have root loading page.
New Guinea Freshwater CrocodileOP
yes thats me :d
loading.tsx for the root layout is for the children route
thats why my demo is showing child route loading.tsx
i thought the IMAGE component thing is something that you want inside a child route 🙃
New Guinea Freshwater CrocodileOP
i dont think that is an issue to be honest, the caching would be the same for the route, woudnt it? even if there is no loading state
yeah it is
lol
not sure how enxtjs handle revalidation
whether if its per request or if the revalidation is stored server-wide
New Guinea Freshwater CrocodileOP
nocache has revalidate = 0, cache has revalidate = 5.
nocache always is ssr, shows loading
cache is same code, except for the revalidate = 5 - and you have to refresh twice again
but the behavior is consistent if you wait for 10second
it works if i wait longer
but no loading screen though
New Guinea Freshwater CrocodileOP
i dont think it works, maye we refreshed both - i have added a 1s delay in the get date function, so either it has to load or show the old content
nocache should show loading
nocache is what im familiar with
but caching and ISR, eugh...
New Guinea Freshwater CrocodileOP
i dont really think there is a solution yet, but that displays the issue as you have to refresh twice (or more, depending on the time the api takes in the background to regenerate the page)
what if you use Suspense?
to manually show the Loading page while the chidl component is awaiting to render
New Guinea Freshwater CrocodileOP
the child, which is generated on the server, loads instantly as the cached version is sent
so it would not change the behavior
actually i think the whole page is wrapped in suspense and the loading.tsx is used as a placeholder
so the logic is already there in this waay
i know but nextjs is doing some caching magickery
New Guinea Freshwater CrocodileOP
😄
just like how in vercel
they always show the previous version right at the moment it is being ISR'd
oh maybe thats why
New Guinea Freshwater CrocodileOP
yes that is the whole problem, you cannot opt out of this behavior.
its an old behavior from the
pages
dir 🙃if i were you id just deal with it imo
would love to see in the
fetch
option to opt-out of this behaviorNew Guinea Freshwater CrocodileOP
unstable_cache
might be the savior maybe
as it gets the same revalidate like fetch. have to try fetch behavior with revalidate, if it does what we need. could build a workaround where fetch calls an internal api route?
feels dirty though, will try
New Guinea Freshwater CrocodileOP
revalidate in fetch seems to work the same way sadly
(╯°□°)╯︵ â”»â”â”»
yep i've tried both, and also having the same problem
New Guinea Freshwater CrocodileOP
so, only way is to use dynamic SSR in those cases, no caching, then it works, just more strain on the api
use a lighter framework 😆
New Guinea Freshwater CrocodileOP
Hehe, thing is I am just validating if I might use next for future projects. I really want to. I like the direction it is headed. But small things like this make me hesitate…
Well to be fair you can always reside to using only SSR (as done previously or by many other frameworks) or doing it on the clientside
The problem is not the technology, the problem is the documentation that pushes you a certain why which might not be a solution to your problem
I assume being a beginner trying out next, must be very confusing nowadays with the pages / app dichotomy
You could technically say that with any framework, you just pick a different poison
New Guinea Freshwater CrocodileOP
well, the good thing is, that i have touched next for the first time after the app router being stable, so i have not learned anything about the pages router and it was easy to understand what is going on as i didnt have to unlearn anything. i could easily implement an own cache for the api call that just stores the result in memory. <- that is what i would say if i knew the deployment was just a running node process. however i am not sure with vercel, there is a lot of magic and serverless stuff going on, so there might not be a persistent memory. have to try that.
and the last thing i want to do is build parts that are relying on the way it is deployed, that will come back to haunt me at some point
that uses a module that just holds a count that increments with each call - if there is just a long running node process, it should always increment when reloading. if not, it should reset at some point to 0
but it looks like it works like expected
New Guinea Freshwater CrocodileOP
so what i gathered in #discussions - a serverless function is running, but it might shut down at any time resetting the counter or it might scale up and then we have two counters. but that still means, that if we do own in memory caching of the results and have an SSR route, the api request will be made much less then using the build in fetch SSR
i would say, better, not perfect, but very easy to implement