此乃多数教程止步之处。然若汝欲尝试use()于客户端组件中创制一诺,则必遇隐微而恼人之弊。
// Bug: creates a new promise on every render
function UserProfile({ userId }: { userId: string }) {
const user = use(fetchUser(userId)); // new promise every render
return <ProfileCard user={user} />;
}
fetchUser(userId)每渲染必返新承诺。React睹新承诺,复悬止,组件再渲染,造新承诺,复悬止,循环无已。
之用,不取数据,惟读其诺。此诺须于屡现之间,恒守其一。若每现辄造新诺,则永陷悬停之困。
何以固诺
道有数途,各适其构:
一、于上体或伺者中造诺
// Server Component - promise created once, stable across renders
export default function UserPage({ params }: { params: { id: string } }) {
const userPromise = fetchUser(params.id);
return (
<Suspense fallback={<Skeleton />}>
<UserProfile userPromise={userPromise} />
</Suspense>
);
}
否async/await无需,此诺未决而承之。客组件以用之解其缚,主组件不更,故其诺之引固常。
二、用模块之缓存
客组件欲发之,当蓄其诺,俾后呼复得同引:
const cache = new Map<string, Promise<User>>();
function fetchUserCached(id: string): Promise<User> {
if (!cache.has(id)) {
cache.set(id, fetchUser(id));
}
return cache.get(id)!;
}
function UserProfile({ userId }: { userId: string }) {
const user = use(fetchUserCached(userId));
return <ProfileCard user={user} />;
}
同论必得同诺,无循环之患
勿于缓存包装中用异步
勿标缓存函数为异步。异步之辞必生新诺,纵返缓存之值。宜用同步函数,存原诺而返之。
3. 用数据获取之库
如TanStack Query或SWR之库,可自备缓存、去重、再验证之能。其早于use()之用,解更广之题——然亦增~13kB之gzip压缩及提供器之裹。若简以"取一次、显其果"之式,用五行缓存之函数(前述选项二),无额外依存亦可成事。此库之值,在于UI有长存之客户端状态,需常新之境:思仪表盘于标签聚焦时重取,分页之列,或应乐观更新相关查询之变。
四、用React之cache()于Server Components
React为Server Components备有内置之cache()函数。此函数可于单次服务器请求之期间内,将函数之返回值予以备忘:
import { cache } from "react";
const getUser = cache(async (id: string): Promise<User> => {
const res = await fetch(`/api/users/${id}`);
return res.json();
});
多组件调用getUser("123") 同一渲染时,共享一 fetch。缓存限于请求,每新页加载即重置。
cache() 与 useMemo
二者皆可备忘。然 cache() 跨组件于服务器渲染(去重),而 useMemo 独于单组件跨重绘。cache() 适于数据获取,useMemo 适于计算。工不同,事各异。












