该技能是 react 的子技能,专注于 React 19 独有的 hooks 与 <Suspense> API 表面:说明每个 API 接受什么、返回什么、会挂起什么,以及何时使用。Dean-stack 特有的 <Suspense> + use(idbHydrationPromise) 在应用外壳上的接线由 idb 技能负责(它产生 promise 与 Suspense 模式);本技能仅描述该集成所使用的 API。
何时调用:在渲染中从 promise 读取值——use(promise);把一次更新拆分为紧急与非紧急部分——useTransition、useDeferredValue;在真实变更落地前先显示即时 UI 更新——useOptimistic;接线表单 action 并读取其 pending/结果状态——useActionState、useFormStatus;编写或移动非 dean-stack 根级的 <Suspense> 边界(根级由 idb 拥有,它产生 idbHydrationPromise 与应用外壳的 Suspense 接线)。
归属:该栈鼓励使用的 React 19 原始能力——use(promise)、useTransition、useDeferredValue、useOptimistic、useActionState、useFormStatus 与 <Suspense> 边界。
让位于:react(父技能)——版本锁定与路由;react-compiler-rules——关于“不要在 use(promise) 读取周围使用 useMemo”,因为 Compiler 已做记忆化;idb——dean-stack 根 <Suspense> + use(idbHydrationPromise) 集成,本技能描述 use(promise) 做什么,idb 拥有 promise 本身、应用外壳的 Suspense 模式、防抖写穿与 BroadcastChannel 重新水合;jotai——atomWithIDB 工厂契约,在 use(idbHydrationPromise) resolve 后消费已水合状态;tanstack-router-routing——路由组件/loader 内部的 <Suspense> 边界。
Dean-stack 规则:Pillar 3(IDB 优先状态)意味着恰好有一个根 <Suspense> 边界,在应用外壳处调用一次 use(idbHydrationPromise)。它 resolve 之后,每个 atomWithIDB 同步读取其初始值——没有逐 atom 的 suspense。Pillar 4(CLI-gate 优先)意味着一个抛出未处理拒绝的 use(promise) 会冒泡到最近错误边界;在 dev 中该表面算作与 TS 错误同等严重级别的 gate 失败,因此风险 promise 要包在错误边界中。React Compiler 纯度意味着 useTransition 的 start 回调在渲染路径中没问题;useOptimistic 的 reducer 是 (state, action) 的纯函数——与 Jotai 派生相同的纯度规则。不使用 Server Components(仅 SPA)。useFormStatus 在由 action prop 驱动的客户端 <form> 元素内工作;此仓库中没有 RSC 表单集成。
模式:use(promise) 内联读取稳定 Promise(promise 必须在多次渲染间稳定,在模块作用域、父 atom 或父 effect 中创建,绝不要在子渲染中 new Promise();dean-stack 模式是在模块加载时创建一次并在根外壳 use 它);useTransition 标记非紧急更新(input 更新是紧急的,下游过滤重算是非紧急的,pending 适合用于 aria-busy);useDeferredValue 读取略微过期的派生值(快表面读 query,昂贵表面读 deferredQuery);useOptimistic 在写入提交前展示结果(reducer 为纯函数,提交时 React 会把 optimistic 回退为真实值);useActionState 绑定表单 action 并读取结果(action 是 (prevState, formData) => Promise<nextState> reducer,pending 在 action 期间为 true);useFormStatus 从子组件读取父表单 pending 状态(从最近的 <form action={...}> 祖先读取,必须渲染在表单内部,来自 react-dom 而非 react);<Suspense> 作为任何 use(promise) 或 async atom 读取的边界;useId 生成用于 ARIA 接线的稳定 ID。
反模式:不要从子渲染向 use() 传入新分配的 Promise——use(fetch(url)) 每次渲染创建新 Promise 并循环;不要把每个组件包在 <Suspense> 里做 IDB 水合——根 <Suspense> + use(idbHydrationPromise) 只运行一次,逐 atom suspense 会破坏同步读取保证;不要在 use(promise) 调用周围使用 useMemo——React Compiler 会处理记忆化,手动 memo 可能掩盖过期 Promise bug;不要对紧急更新使用 useTransition——受控输入的键入必须保持紧急,只把下游派生包进 startTransition;不要在拥有 <form> 的同一组件中使用 useFormStatus——它返回 { pending: false, data: null, ... },因为没有父表单,要移到子组件;不要写轮询 Promise 的 useEffect——use(promise) + <Suspense> 是 React 19 方式,effect+poll 是 React 17 惯用法;不要从 react 以外的地方导入 Suspense——Suspense 只有一个且在 react 中,react-dom 只导出 useFormStatus。
触发词:use(promise)、useTransition、useDeferredValue、useOptimistic、useActionState、useFormStatus、Suspense、react 19 hook。