前端开发
在已有 TanStack Query 的 Next.js…
当项目已使用 TanStack Query 并需要 next-safe-action 的 mutation 支持缓存失效、重试、DevTools 或离线持久化时,用 adapter 的 mutationOptions() 包装无状态、非抛错型 action。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 59/100 ×0.15
适用场景
在已有 TanStack Query 的特定
解决思路
- 安装依赖:npm install @next-safe-action/adapter-tanstack-query @tanstack/react-query
- 从 @next-safe-action/adapter-tanstack-query 导入 mutationOptions、ActionMutationError、isActionMutationError、hasServerError、hasValidationErrors
- 在客户端组件中用 useMutation(mutationOptions(createUser, { onSuccess })) 包装 action,通过 mutate() 传入输入,用 isPending/isError/error/data 跟踪状态
- 理解 mutationOptions() 的桥接机制:调用 safe action → 检查结果 envelope 中的 serverError 或 validationErrors → 若存在则抛出 ActionMutationError(客户端错误类,instanceof 可用)→ 成功时直接返回 data 作为 TData → 通过组合 throwOnError 在 React 渲染阶段重新抛出 redirect()/notFound() 等导航错误
- 用 returnServerError() 产生的预期错误会进入 serverError,并以带类型 payload 的 ActionMutationError 形式暴露,与 mutationOptions() 完全兼容(不同于 throwServerError)
- 按场景选择:新项目无 TanStack Query、简单表单/按钮、useOptimistic 即时乐观 UI、需排队累积的重叠写入(排序、看板)用内置 hooks;数据独立于用户变化(socket、轮询、后台刷新)时读取用 TanStack Query/SWR、mutation 用 adapter;已用 TanStack Query 或 tRPC + TanStack Query、需要自动重试退避、mutation 后失效客户端查询缓存、TanStack Query DevTools、离线 mutation 持久化时用 adapter
- 注意功能差异:内置 hooks 在 startTransition 中运行、用 useOptimisticAction 做乐观更新、无自动重试、无客户端缓存失效、无 DevTools、无离线持久化、用结果 envelope 错误模型、executeAsync() 返回 Promise<Result>、用 status 字符串跟踪;adapter 无 Transitions、需手动 onMutate + 查询缓存做乐观更新、支持 retry 退避、queryClient.invalidateQueries() 失效客户端缓存、有 DevTools、抛 ActionMutationError 配合类型守卫、支持 dehydrate/hydrate 离线持久化、mutateAsync() 返回 Promise<Data>、用布尔标志跟踪
- 总体建议:多数 Next.js 应用优先用内置 hooks(无额外依赖、集成 React 并发渲染);当 TanStack Query 已在技术栈中,尤其需要缓存失效、重试、DevTools、离线支持时优先用 adapter
- 只做 mutation:不要用 queryOptions(),因为 Server Actions 仅支持 POST 不适合 GET 查询、按客户端排队造成请求瀑布、POST 绕过浏览器缓存/ETag/条件请求、无稳定资源标识供去重;数据获取改用 Server Components(服务端)、Route Handlers + useQuery(客户端)或 tRPC(全栈类型安全)
- 仅适用于无状态 .action() 函数:mutationOptions() 接受 SingleInputActionFn,无法包装 .stateAction();有状态 action 请用 next-safe-action/hooks 的 useStateAction / useOptimisticStateAction
- 仅适用于非抛错型 action:不要对传给 mutationOptions() 的 action 使用 throwValidationErrors: true 或 throwServerError: true,否则 adapter 无法从结果 envelope 提取结构化错误数据、丢失类型安全错误处理;TypeScript 通过 NonThrowingActionConstraint 强制此约束
- 避免反模式:不要用 throwValidationErrors 配合 adapter(会触发 TypeScript 错误)、不要用 mutationOptions 做数据获取(应改用 Route Handler + useQuery)、不要在 mutationFn 中手动调用 action(会丢失错误桥接与导航处理),正确做法是 useMutation(mutationOptions(myAction))
适用边界 · 注意事项
- 项目未使用 TanStack Query 且不需要缓存失效、重试、DevTools 或离线持久化时,优先用 next-safe-action 内置 hooks
- 需要对有状态 action(.stateAction())做 mutation 时,改用 useStateAction / useOptimisticStateAction
- action 使用了 throwValidationErrors: true 或 throwServerError: true 时,adapter 无法提取结构化错误,不适用