Files
2026-07-13 21:36:17 +08:00

77 lines
2.2 KiB
Markdown

---
title: Per-Request Deduplication with React.cache()
impact: MEDIUM
impactDescription: deduplicates within request
tags: server, cache, react-cache, deduplication
---
## 使用 React.cache() 实现单次请求内的去重
使用 `React.cache()` 实现服务端请求去重。身份验证和数据库查询受益最大。
**用法:**
```typescript
import { cache } from 'react'
export const getCurrentUser = cache(async () => {
const session = await auth()
if (!session?.user?.id) return null
return await db.user.findUnique({
where: { id: session.user.id }
})
})
```
在单次请求中,多次调用 `getCurrentUser()` 仅执行一次查询。
**避免使用内联对象作为参数:**
`React.cache()` 使用浅相等(`Object.is`)来判断缓存是否命中。内联对象每次调用都会创建新的引用,导致无法命中缓存。
**错误做法(总是缓存未命中):**
```typescript
const getUser = cache(async (params: { uid: number }) => {
return await db.user.findUnique({ where: { id: params.uid } })
})
// 每次调用都创建新对象,永远不会命中缓存
getUser({ uid: 1 })
getUser({ uid: 1 }) // 缓存未命中,再次执行查询
```
**正确做法(缓存命中):**
```typescript
const getUser = cache(async (uid: number) => {
return await db.user.findUnique({ where: { id: uid } })
})
// 原始类型参数使用值相等判断
getUser(1)
getUser(1) // 缓存命中,返回缓存结果
```
如果必须传递对象,请传递同一个引用:
```typescript
const params = { uid: 1 }
getUser(params) // 执行查询
getUser(params) // 缓存命中(同一引用)
```
**Next.js 特别说明:**
在 Next.js 中,`fetch` API 已自动扩展了请求记忆化功能。相同 URL 和选项的请求在单次请求内会自动去重,因此 `fetch` 调用无需使用 `React.cache()`。但 `React.cache()` 对其他异步任务仍然至关重要:
- 数据库查询(Prisma、Drizzle 等)
- 大量计算
- 身份验证检查
- 文件系统操作
- 任何非 `fetch` 的异步工作
使用 `React.cache()` 在组件树中对这些操作进行去重。
参考文档:[React.cache 文档](https://react.dev/reference/react/cache)