| 1 | --- |
| 2 | title: Per-Request Deduplication with React.cache() |
| 3 | impact: MEDIUM |
| 4 | impactDescription: deduplicates within request |
| 5 | tags: server, cache, react-cache, deduplication |
| 6 | --- |
| 7 | |
| 8 | ## Per-Request Deduplication with React.cache() |
| 9 | |
| 10 | Use `React.cache()` for server-side request deduplication. Authentication and database queries benefit most. |
| 11 | |
| 12 | **Usage:** |
| 13 | |
| 14 | ```typescript |
| 15 | import { cache } from 'react' |
| 16 | |
| 17 | export const getCurrentUser = cache(async () => { |
| 18 | const session = await auth() |
| 19 | if (!session?.user?.id) return null |
| 20 | return await db.user.findUnique({ |
| 21 | where: { id: session.user.id } |
| 22 | }) |
| 23 | }) |
| 24 | ``` |
| 25 | |
| 26 | Within a single request, multiple calls to `getCurrentUser()` execute the query only once. |
| 27 | |
| 28 | **Avoid inline objects as arguments:** |
| 29 | |
| 30 | `React.cache()` uses shallow equality (`Object.is`) to determine cache hits. Inline objects create new references each call, preventing cache hits. |
| 31 | |
| 32 | **Incorrect (always cache miss):** |
| 33 | |
| 34 | ```typescript |
| 35 | const getUser = cache(async (params: { uid: number }) => { |
| 36 | return await db.user.findUnique({ where: { id: params.uid } }) |
| 37 | }) |
| 38 | |
| 39 | // Each call creates new object, never hits cache |
| 40 | getUser({ uid: 1 }) |
| 41 | getUser({ uid: 1 }) // Cache miss, runs query again |
| 42 | ``` |
| 43 | |
| 44 | **Correct (cache hit):** |
| 45 | |
| 46 | ```typescript |
| 47 | const getUser = cache(async (uid: number) => { |
| 48 | return await db.user.findUnique({ where: { id: uid } }) |
| 49 | }) |
| 50 | |
| 51 | // Primitive args use value equality |
| 52 | getUser(1) |
| 53 | getUser(1) // Cache hit, returns cached result |
| 54 | ``` |
| 55 | |
| 56 | If you must pass objects, pass the same reference: |
| 57 | |
| 58 | ```typescript |
| 59 | const params = { uid: 1 } |
| 60 | getUser(params) // Query runs |
| 61 | getUser(params) // Cache hit (same reference) |
| 62 | ``` |
| 63 | |
| 64 | **Next.js-Specific Note:** |
| 65 | |
| 66 | In Next.js, the `fetch` API is automatically extended with request memoization. Requests with the same URL and options are automatically deduplicated within a single request, so you don't need `React.cache()` for `fetch` calls. However, `React.cache()` is still essential for other async tasks: |
| 67 | |
| 68 | - Database queries (Prisma, Drizzle, etc.) |
| 69 | - Heavy computations |
| 70 | - Authentication checks |
| 71 | - File system operations |
| 72 | - Any non-fetch async work |
| 73 | |
| 74 | Use `React.cache()` to deduplicate these operations across your component tree. |
| 75 | |
| 76 | Reference: [React.cache documentation](https://react.dev/reference/react/cache) |
| 77 |