Closed EvandrooViegas closed 6 months ago
crazy that it's still an issue...
this is so annoying
This makes app router unusable anything other than static pages. There is always a hydration error in dynamic pages that force root to client rendering which is basically plain react.
Using "router.refresh" worked for me but I don't like how my progress bar stoped working too
Hi everyone, will be taking a look at this!
It seems the original that provided is currently a 404
(not a or doesn't exist anymore).
If anyone is able to provide a minimal , that will be great so we can confirm and fix this long-standing issue.
@samcx How do we opt-out of client-side caching?
@ChoaibMouhrach I believe opting-out of client-side caching is related to the original issue above because the client-side cache is different from the cache used by fetch. The only way to currently do this is using router.refresh
.
Can you clarify why you wish to opt-out of client-side cache?
@samcx In my case, I have a sidebar with links. When I navigate between those links, I retrieve fresh data from the database. However, when I create a new item in the database and then navigate back to the page via a link component, I don't see the newly created item. This is an issue for me. The only solution I found is using a router.refresh, but I encountered some drawbacks, one of which is the loss of my page navigation progress bar.
@ChoaibMouhrach My apologies, I take back what I said earlier—revalidatePath
is indeed another option for purging Client-side Router Cache.
In your use-case, you could use revalidatePath
(https://nextjs.org/docs/app/api-reference/functions/revalidatePath) instead of router.refresh
. Are you using a Server Action to retrieve fresh data from the database?
I believe this will not cause the loss of your page navigation progress bar.
@samcx Yes I'm using server actions to get the data, I think I tried it before and it didn't work, I'll give it a shot then I'll let you know
@samcx I just had the time to try it, unfortunately, it didn't work, I still get the old data.
@ChoaibMouhrach Thanks for testing!
That seems like possibly a separate issue with revalidatePath
. Can you submit a new bug so we can take a look?
@samcx, Thanks, I will
hey it's been a while! I've been doing some testing and I think the issue isn't a problem anymore. Turns out everything works just fine when unstable_cache
and revalidateTag
are used correctly.
I've been experimenting and testing this in my own repo, nextjs-caching-with-prisma. If you've got some time, feel free to clone it and give it a shot.
To give you a quick rundown, here's what I've done.
First, I put the caching and re-validating logic in the src/lib/post.ts
file:
// ...
import { unstable_cache as cache, revalidateTag } from "next/cache";
export const getPosts = cache(
async () => {
return await prisma.post.findMany({
// ...
});
},
["posts"],
{
tags: ["posts"],
}
);
export const createPost = async (data: Prisma.PostCreateInput) => {
const newPost = await prisma.post.create({
data,
});
revalidateTag("posts");
return newPost;
};
// ...
Then, I separated the server action and form validation logic in the src/actions/post.action.ts
file:
"use server"; // server actions have to have the "use server" directive
export const createPostAction = async (prevState: any, formData: FormData) => {
// validation
const newPost = await createPost({
// ...
});
return {
post: newPost,
};
};
Lastly, you could use a form
to call the server action, like this:
"use client";
export async function PostCreationForm() {
const [state, formAction] = useFormState(createPostAction, {
errors: {},
});
useEffect(() => {
if (state.post) {
redirect("/posts");
}
}, [state]);
return (
<form
action={formAction}
>
{/* ... */}
</form>
)
}
p.s. If you don't want to use a form
, you could use useTransition
hook to call the server action:
const [isPending, startTransition] = useTransition();
// ...
<Button
variant="destructive"
onClick={() => {
startTransition(async () => {
await deletePostAction(null, post.id);
});
}}
disabled={isPending}
>
Delete
</Button>
@jack-szeto Thanks for sharing!
In the meantime, I'll be closing this issue. If we issue arises again, feel free to create a new bug report so we can take a closer look!
This is definitely not fixed; I've reproduced it with a minimal case here: https://github.com/6bangs/nextjs-bug
This is just npx create-next-app@latest
with the following changes:
Link
tag on the main page:
Link to bug:
<Link href="/bug/3" prefetch={false}>click here</Link>
Dynamic route with optional catch-all segment at app/bug/[[...id]]/page.tsx
:
export const dynamic = "force-dynamic";
type Props = {
params: {
id: string;
}
}
const BugPage = async ({params: {id}}: Props) => {
console.log(`Bug page executing for ID ${id}`);
return (
<p>Testing.</p>
)
}
export default BugPage;
Repro steps:
Bug page executing for ID 3
No combination of dynamic
or revalidate
resolves the issue. I've also tried using revalidatePath
without success.
IMO this ticket should be reopened rather than creating a new one; there are ~30 others who have run into the issue and a lot of useful comments.
@6bangs Thanks for sharing a ! I've reopened the issue.
Will be taking a look soon at the !
@6bangs
I've been looking at your example and I have a few questions. I understand how it might be confusing when a page doesn't rerender despite having dynamic
or revalidate
set.
However, in your example repo, I noticed that nothing changes when you revisit the page. Shouldn't it be cached in this case?
Also, I'm wondering if there are any specific cases where you'd want a dynamic page that doesn't make use of the unstable_cache
and tag
functions? I'm just trying to see if there are any scenarios that I might not have considered.
For my AI use case:
This is my situation, but the bug would be noticed for any asynchronous behavior that modifies the state associated with the segment.
@6bangs This is actually separate from the issue that was originally reported since there is no fetch()
in your example—the export const dynamic = "force-dynamic";
will only affect fetch()` and will not affect the Router Cache. You'll see it log again after 30s. So your example is working as expected.
There will be an upcoming API (staletime
) that will let you customize this, but we are still listening to feedback on this! We also need to improve the observability here, as it is not immediately obvious when the Router Cache was used.
Yup, staletime
would work for my use case. Any ETA on that? For now we're stuck with a hacky workaround (appending a cache-busting parameter to the URI).
Hello! I'm having a similar presumably server-side caching issue (I'm assuming so since the issue persists across different browsers and devices). I have a page for which I get the contents with fetch in a server component, Next version I'm using is 14.1.0
, the route segment config for the whole layout is as follows:
export const dynamic = 'force-dynamic'
export const fetchCach = 'force-no-store'
export const revalidate = false
The issue I'm having is: when the content is updated on the backend (the one I'm fetching from in the RSC) the page does show the new content on hard navigation, but when the same page is opened using client-side navigation all I'm seeing is stale content. For now I just stick to using hard navigation for all of the links, but hopefully I could get some help with the issue
@6bangs No timetable for this yet but hopefully soon!
@run4w4y Can you file a new bug report so we can take a closer look?
@samcx Thank you for your response! I was thinking this might be related to the issue discussed above, I'm sorry if it is not. In that case I'll try to come up with a smaller repro tomorrow and open up another bug report
I am having the same issue with next.js v 14.0.3
. I am using a fetch GET
request from a client side component and the response always has X-Nextjs-Cache: HIT
even with hard refresh. It works fine when I use a dev build - this issue occurs in a production build only.
The only thing which worked was router.refresh()
. It seems that it is a router cache, but I thought that this cache will be used when using e.g. Link
components, but not with a hard refresh of a page in browser. Is that correct behavior of a router cache?
@6bangs No timetable for this yet but hopefully soon!
@run4w4y Can you file a new bug report so we can take a closer look?
More than a month later, still no timetable for fixing this? This had been reported 8 month ago. Please let me know if you are going to fix this or not. I do not want to use 10 hacks to avoid this in different situations, just because somebody is dead set on doing things the wrong way. Just tell me if you are not going to fix this, so that I can move to some other framework, since you seem to be on the path to ruin this. Thank you.
@mirano-galijasevic As far as I'm aware, there were different issues being reported in this thread, and there was no given for the original issue post.
We can't reliably debug and fix and issue without a reproduction. Unfortunately, that's just the case here (according to the original post). There are other bugs and edge cases we're fixing constantly (ones that are older than 8 months or younger), so not saying we're not fixing this, and I'm fully aware of new notifications coming into this thread.
Thanks for watching this issue @samcx yes there are different issues reported here while all of those can be fixed by fixing the revalidate to be transparent and work as it is said to work.
Which is
revalidate:0
0 means 0 - no cache.
And in support of @mirano-galijasevic - I feel your pain and just due to this issue, many times we had to choose a framework other than Next14.
In my case the problem was that the generated route was static content:
In that route I only had a GET
method defined and in this instance every fetch
request to this route was cached regardless of my config (even with a cache boosting parameter). If I add there any dummy HTTP methods there like POST
or PUT
the build will generate this endpoint as dynamic and it works as expected. Or, I could use an edge
runtime too.
@kevincobain2000 Not able to reproduce myself.
(/app/page.tsx
)
export default async function Page() {
const data = await getData();
return (
<div>
<div>{JSON.stringify(data)}</div>
</div>
);
}
async function getData() {
const res = await fetch("https://catfact.ninja/fact", {
next: { revalidate: 0 },
});
if (!res.ok) {
// This will activate the closest `error.js` Error Boundary
throw new Error("Failed to fetch data");
}
return res.json();
}
(/next.config.mjs
)
/**
* @type {import('next').NextConfig}
*/
const nextConfig = {
reactStrictMode: true,
logging: {
fetches: {
fullUrl: true,
},
},
}
export default nextConfig
@samcx In your same example above you are refreshing the page and hitting the same url. That is why there is no cache hit.
If you do a soft navigation then it doesn't hit.
npx create-next-app -e https://github.com/nextui-org/next-app-template
page.tsx
async function getData() {
const res = await fetch("https://catfact.ninja/fact", {
next: { revalidate: 0 },
});
if (!res.ok) {
// This will activate the closest `error.js` Error Boundary
throw new Error("Failed to fetch data");
}
return res.json();
}
export default async function Home() {
const data = await getData();
return (
<section className="flex flex-col items-center justify-center gap-4 py-8 md:py-10">
<div>{JSON.stringify(data)}</div>
...
and so on
GET / 200 in 246ms
│ GET https://catfact.ninja/fact 200 in 211ms (cache: SKIP)
│ │ Cache missed reason: (revalidate: 0)
⚠ Unsupported metadata themeColor is configured in metadata export. Please move it to viewport export instead.
⚠ Unsupported metadata themeColor is configured in metadata export. Please move it to viewport export instead.
GET /robots.txt?1709255277972 404 in 35ms
GET /about?_rsc=acgkz 200 in 26ms
⚠ Unsupported metadata themeColor is configured in metadata export. Please move it to viewport export instead.
GET /about?_rsc=13o5j 200 in 17ms
GET /?_rsc=1mruw 200 in 15ms <---
Reproduce
@kevincobain2000 Just verified what you're seeing.
This is the same thing as mentioned above—the fetch isn't being cached, but there's a separate Router Cache that's caching in-memory. Sounds kind of unintuitive that that we're caching the fetch through the Router Cache that wasn't cached on the server, but that's because one is done on the Client versus the Server.
https://nextjs.org/docs/app/building-your-application/caching#overview
Also to clarify, this isn't something we just created for App Router—there also was a similar Router Cache in Pages Router. You just wouldn't come across the need to bypass/bust that cache (e.g., in the rare case you on-demand ISR, and want to bust that route's cache too on the client, not really necessary).
Besides the hacky solution above, I think what we need is the Staletime API which was mentioned above, and a more ergonomic way to possibly opt-out the Router Cache.
@samcx Thanks for verifying.
Understand what you said, you have put it nicely.
Yes, there needs to be a simple setting that can turn this cache on, off, or for duration.
The reason it has become a big issue, is that most users believe that revalidate
was for this purpose. And not all users want a router cache, especially when navigation expects data change - trading apps, admin panels, or any other case.
I've just chased down an error where SSR data fetching ceased for a specific simple URL. My error was caused by the router keeping an entry permanently in the router.js "Server Data Cache" (this.sdc/inflightCache). The error is caused by executing multiple rapid "router.push(" calls to pages with SSR. If the first router.push data fetch call is cancelled at exactly the right time (just before it's deleted from the inflight cache) the data gets stuck in the inflight request cache and will be served for all subsequent navigations to the SSR page.
The symptom for the error in this scenario will be a client-side console error "Loading initial props cancelled".
The "inflightCache" appears to only be active for production builds. In my case it only manifested when running end-to-end tests against staging that were navigating very rapidly between pages.
@kevincobain2000 Staletime API (experimental) is available now → https://nextjs.org/docs/app/api-reference/next-config-js/staleTimes.
In the meantime, I will be closing this issue for the time being—feel free to create a new bug report if this is an issue un-related to router cache.
Thanks @samcx I ll check this out.
@samcx, I see this:
The dynamic property is used when the prefetch prop on Link is left unspecified. The static property is used when the prefetch prop on Link is set to true, or when calling router.prefetch.
What about in my example where prefetch is false?
@6bangs good callout! I have created a to update our → https://github.com/vercel/next.js/pull/65252.
Great, thanks!
This closed issue has been automatically locked because it had no new activity for 2 weeks. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.
Verify canary release
Provide environment information
Which area(s) of Next.js are affected? (leave empty if unsure)
App directory (appDir: true), Data fetching (gS(S)P, getInitialProps)
Link to the code that reproduces this issue or a replay of the bug
https://github.com/EvandrooViegas/outsystem-angola
To Reproduce
npm install
in the root of the cloned reponpm run dev
Describe the Bug
Next13 seems to be caching the data coming from supabase even with the
revalidate = 0
route segment.Expected Behavior
Get the freshest data in every page request
Which browser are you using? (if relevant)
chrome
How are you deploying your application? (if relevant)
No response