Nested unstable_cache results are not cached. #81128
khobiziilyes
started this conversation in
Ideas
Replies: 1 comment
-
This is currently expected behaviour. Inner cache scopes are not created when using |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
Link to the code that reproduces this issue
https://github.com/khobiziilyes/unstable-cache-reprod
To Reproduce
Current vs. Expected behavior
I've tried to simplify the flow of the issue as much as possible.
The project consists of 6 main commits. The first 2 are unimportant (t3app bootstrapping). The 3rd commit introduces the minimal and most basic structure of the app, which is:
The app has 3 main "api" functions (
apis.ts
)getBrands
: returns an array ofbrandId
.getBrandDetails
: receives theid
of the brand, returns thename
anddescription
.getSuggestedBrands
: For simplicity, it returns all brands except the one you supply. In a real case scenario, it would calculate the similarity to the other brands using the Levenshtein algo. Notice that THIS IS the critical function, since it implements another function,getBrands
, inside it.All of those have a
console.log
to simplify debugging.The main page of the app (
/
) displays a list of links to all the brands available, which are retrieved usinggetBrands()
.Each Brand page (
/[brandId]
) displays the details of the current brand, and also displays other suggested brands to visit which is a Suspended Server component that usesgetSuggestedBrands
.If we run our app (npm run preview) and visit the main page, we'll notice that
getBrands
is not called (The whole page is static on build, the code is not running at all).Now, we want to view a single brand page. If we visit the brands 1, 2, and 3 respectively, we'll see those logs:
And this is perfectly normal since we are not doing ISR.
Notice that each
getSuggestedBrands
callsgetBrands
and uses that value.Now, since those "api" calls are expensive, we have decided to add a cache layer to those functions, and since they are not "fetch" calls. We'll use unstable_cache instead (not React.cache because we want it to be shared among all requests). And that's what the 4th commit is for, we wrapped the
getBrands
andgetBrandDetails
inunstable_cache
. We will rebuild the app and visit the brands pages again, we will notice those logs:We can notice that
getBrands
is not called anymore, because it is already called at build time and cached. And now the cached value is used in all calls, including the ones thatgetSuggestedBrands
invokes.If we refresh the page
/3
, we will even notice thatgetBrandDetails(3)
is not called anymore, since the first visit to the url invoked and cached the value. Super!However, we have one more function left that is not cached, and in real life, such function will consume a lot of processing power, and we'd love to cache the final result of it.
getSuggestedBrands
, this function has only 1 parameter,currentBrandId
, which it uses to find more interesting brands similar to that one.Commit 6: Introduces the issue.
After we wrap the function (which inherently uses another cached function
getBrands
), visit the pages, we'll get those logs:getBrands
is called over and over ... If we try to use this function on its own, we'll get the cached value, but if we use it insidegetSuggestedBrands
, the cached value is not retrieved.getSuggestedBrands
is cached as expected.Provide environment information
Operating System: Platform: darwin Arch: arm64 Version: Darwin Kernel Version 23.6.0: Thu Mar 6 22:08:50 PST 2025; root:xnu-10063.141.1.704.6~1/RELEASE_ARM64_T8112 Available memory (MB): 16384 Available CPU cores: 8 Binaries: Node: 22.15.0 npm: 11.4.1 Yarn: N/A pnpm: N/A Relevant Packages: next: 15.4.0-canary.82 // Latest available version is detected (15.4.0-canary.82). eslint-config-next: 15.3.3 react: 19.1.0 react-dom: 19.1.0 typescript: 5.8.3 Next.js Config: output: N/A
Which area(s) are affected? (Select all that apply)
Use Cache
Which stage(s) are affected? (Select all that apply)
next dev (local), next start (local), next build (local), Vercel (Deployed), Other (Deployed)
Additional context
Before each preview, I run
rm -rf .next/
to make sure no cached values will mess with the reproduction.I haven't dug deep into Next.js code, but I'd like to help resolve this issue if possible.
Beta Was this translation helpful? Give feedback.
All reactions