nextjs - ✅(Solved) Fix Docs: [Cache Components] "use cache" pitfalls with map() [2 pull requests, 7 comments, 4 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
vercel/next.js#86896Fetched 2026-04-08 02:08:30
View on GitHub
Comments
7
Participants
4
Timeline
24
Reactions
5
Author
Timeline (top)
commented ×7subscribed ×5referenced ×4cross-referenced ×2

Fix Action

Fixed

PR fix notes

PR #86920: Omit unused arguments from 'use cache' function calls

Description (problem / solution / changelog)

If we know the declared parameters of a 'use cache' function from static analysis, we can omit any unused arguments from the generated call to the cache wrapper. This avoids issues where extra arguments, like the index and array in a Array.prototype.map() call, would cause unexpected cache misses because those would also be included in the cache key, and they might not be deterministic.

In #72506, we already implemented omitting unused arguments for calls to 'use cache' functions from the client, but the optimization of this PR now affects all calls to 'use cache' functions, including those from the server. We do keep the change from #72506 regardless, as that is still a useful optimization to reduce the payload that's sent to the server.

Functions that can't be statically analyzed, e.g. because they're re-exports, or results of higher-order functions, will still receive all arguments as before, so they won't benefit from this optimization.

Note that the optimization is safe because we already forbid using arguments in 'use cache' functions at compile time.

The implementation is trivial thanks to the output changes we implemented in #85519.

addresses #86896

Changed files

  • crates/next-custom-transforms/src/transforms/server_actions.rs (modified, +38/-10)
  • crates/next-custom-transforms/tests/fixture/next-font-with-directive/use-cache/output.js (modified, +1/-1)
  • crates/next-custom-transforms/tests/fixture/server-actions/next.d.ts (modified, +1/-1)
  • crates/next-custom-transforms/tests/fixture/server-actions/server-graph/33/output.js (modified, +1/-1)
  • crates/next-custom-transforms/tests/fixture/server-actions/server-graph/34/output.js (modified, +4/-4)
  • crates/next-custom-transforms/tests/fixture/server-actions/server-graph/35/output.js (modified, +1/-1)
  • crates/next-custom-transforms/tests/fixture/server-actions/server-graph/36/output.js (modified, +4/-4)
  • crates/next-custom-transforms/tests/fixture/server-actions/server-graph/37/output.js (modified, +1/-1)
  • crates/next-custom-transforms/tests/fixture/server-actions/server-graph/38/output.js (modified, +1/-1)
  • crates/next-custom-transforms/tests/fixture/server-actions/server-graph/39/output.js (modified, +1/-1)
  • crates/next-custom-transforms/tests/fixture/server-actions/server-graph/40/output.js (modified, +1/-1)
  • crates/next-custom-transforms/tests/fixture/server-actions/server-graph/41/output.js (modified, +1/-1)
  • crates/next-custom-transforms/tests/fixture/server-actions/server-graph/42/output.js (modified, +1/-1)
  • crates/next-custom-transforms/tests/fixture/server-actions/server-graph/43/output.js (modified, +1/-1)
  • crates/next-custom-transforms/tests/fixture/server-actions/server-graph/45/output.js (modified, +1/-1)
  • crates/next-custom-transforms/tests/fixture/server-actions/server-graph/46/output.js (modified, +3/-3)
  • crates/next-custom-transforms/tests/fixture/server-actions/server-graph/48/output.js (modified, +1/-1)
  • crates/next-custom-transforms/tests/fixture/server-actions/server-graph/49/output.js (modified, +1/-1)
  • crates/next-custom-transforms/tests/fixture/server-actions/server-graph/50/output.js (modified, +1/-1)
  • crates/next-custom-transforms/tests/fixture/server-actions/server-graph/52/output.js (modified, +2/-2)
  • crates/next-custom-transforms/tests/fixture/server-actions/server-graph/53/output.js (modified, +1/-1)
  • crates/next-custom-transforms/tests/fixture/server-actions/server-graph/54/output.js (modified, +1/-1)
  • crates/next-custom-transforms/tests/fixture/server-actions/server-graph/55/output.js (modified, +1/-1)
  • crates/next-custom-transforms/tests/fixture/server-actions/server-graph/56/output.js (modified, +1/-1)
  • crates/next-custom-transforms/tests/fixture/server-actions/server-graph/57/output.js (modified, +1/-1)
  • crates/next-custom-transforms/tests/fixture/server-actions/server-graph/58/output.js (modified, +1/-1)
  • crates/next-custom-transforms/tests/fixture/server-actions/server-graph/60/output.ts (modified, +1/-1)
  • crates/next-custom-transforms/tests/fixture/server-actions/server-graph/63/output.tsx (modified, +9/-9)
  • crates/next-custom-transforms/tests/fixture/server-actions/server-graph/64/output.js (modified, +1/-1)
  • crates/next-custom-transforms/tests/fixture/server-actions/server-graph/65/output.js (modified, +1/-1)
  • crates/next-custom-transforms/tests/fixture/server-actions/server-graph/67/output.js (modified, +1/-1)
  • crates/next-custom-transforms/tests/fixture/server-actions/server-graph/68/output.tsx (modified, +2/-2)
  • crates/next-custom-transforms/tests/fixture/source-maps/server-graph/use-cache/1/output.js (modified, +2/-2)
  • packages/next/src/server/use-cache/use-cache-wrapper.ts (modified, +2/-4)
  • test/e2e/app-dir/use-cache-custom-handler/use-cache-custom-handler.test.ts (modified, +1/-1)
  • test/e2e/app-dir/use-cache/app/(dynamic)/unused-args/page.tsx (added, +24/-0)
  • test/e2e/app-dir/use-cache/use-cache.test.ts (modified, +19/-10)
  • test/production/custom-server/custom-server.test.ts (modified, +1/-1)

PR #86937: docs: use cache debugging notes

Description (problem / solution / changelog)

Closes: https://github.com/vercel/next.js/issues/86896

Changed files

  • docs/01-app/03-api-reference/01-directives/use-cache.mdx (modified, +18/-0)

Code Example

const resolveDidToHandle = cache(async function resolveDidToHandle(did: string): Promise<string> {
   "use cache: redis";
   tagDid(did);
@@ -156,7 +160,7 @@ export const resolveHandleToDid = cache(async function resolveHandleToDid(
 
 async function batchResolveHandles(dids: string[]): Promise<Map<string, string>> {
   const unique = [...new Set(dids)];
-  const results = await Promise.all(unique.map(resolveDidToHandle));
+  const results = await Promise.all(unique.map((did) => resolveDidToHandle(did)));
   return new Map(unique.map((did, i) => [did, results[i]]));
 }
 
@@ -191,8 +195,8 @@ async function batchResolveUsers(dids: string[]): Promise<Map<string, User>> {
   if (unique.length === 0) return new Map();
 
   const [handles, avatars] = await Promise.all([
-    Promise.all(unique.map(resolveDidToHandle)),
-    Promise.all(unique.map(tryFetchBskyAvatar)),
+    Promise.all(unique.map((did) => resolveDidToHandle(did))),
+    Promise.all(unique.map((did) => tryFetchBskyAvatar(did))),
   ]);
RAW_BUFFERClick to expand / collapse

What is the documentation issue?

Here's a fix I'm going to deploy now:

 const resolveDidToHandle = cache(async function resolveDidToHandle(did: string): Promise<string> {
   "use cache: redis";
   tagDid(did);
@@ -156,7 +160,7 @@ export const resolveHandleToDid = cache(async function resolveHandleToDid(
 
 async function batchResolveHandles(dids: string[]): Promise<Map<string, string>> {
   const unique = [...new Set(dids)];
-  const results = await Promise.all(unique.map(resolveDidToHandle));
+  const results = await Promise.all(unique.map((did) => resolveDidToHandle(did)));
   return new Map(unique.map((did, i) => [did, results[i]]));
 }
 
@@ -191,8 +195,8 @@ async function batchResolveUsers(dids: string[]): Promise<Map<string, User>> {
   if (unique.length === 0) return new Map();
 
   const [handles, avatars] = await Promise.all([
-    Promise.all(unique.map(resolveDidToHandle)),
-    Promise.all(unique.map(tryFetchBskyAvatar)),
+    Promise.all(unique.map((did) => resolveDidToHandle(did))),
+    Promise.all(unique.map((did) => tryFetchBskyAvatar(did))),
   ]);

Without this fix, my Redis cache handler wasn't working properly.

Can you spot why?

Is there any context that might help us understand?

I'm honestly not sure if this is something to call out in the docs, if this could be linted against, or something else. But it was extremely confusing. (To clarify, the issue is that map sends the array and the index to the mapped function, which implicitly become cache keys, blowing up the cache size and making it miss often.)

For what it's worth, a mode that simply logs the captured cache keys would've helped here.

Does the docs page already exist? Please link to it.

https://nextjs.org/docs/app/api-reference/directives/use-cache#cache-keys

extent analysis

TL;DR

Explicitly passing the did parameter to the resolveDidToHandle function within the map callback ensures that the cache key is correctly generated based on the did value.

Guidance

  • The issue arises because map passes the array element and index to the callback function, which can lead to incorrect cache keys being generated.
  • To verify the fix, monitor the cache size and hit rate after applying the change to ensure that the cache is working as expected.
  • Consider adding a logging mode to capture cache keys for debugging purposes, as suggested in the issue.
  • Review the documentation on cache keys (https://nextjs.org/docs/app/api-reference/directives/use-cache#cache-keys) to understand how cache keys are generated and how to optimize cache usage.

Example

const results = await Promise.all(unique.map((did) => resolveDidToHandle(did)));

This code snippet demonstrates the explicit passing of the did parameter to the resolveDidToHandle function, ensuring correct cache key generation.

Notes

The provided fix assumes that the did value is the intended cache key. If other parameters or values should be used to generate the cache key, additional modifications may be necessary.

Recommendation

Apply the workaround by explicitly passing the did parameter to the resolveDidToHandle function, as shown in the example code snippet, to ensure correct cache key generation and optimal cache usage.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

nextjs - ✅(Solved) Fix Docs: [Cache Components] "use cache" pitfalls with map() [2 pull requests, 7 comments, 4 participants]