一键导入
frontend-mutation-hooks
Use when creating or using TanStack Query mutations for data modifications
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Use when creating or using TanStack Query mutations for data modifications
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
| name | frontend-mutation-hooks |
| description | Use when creating or using TanStack Query mutations for data modifications |
Clean, minimal mutation hooks for data modifications. NO comments - self-documenting code through naming and types.
Hook: useM_[Scope]_[EntityAction] | Variable: m[EntityAction] (drop scope)
const mProjectCreate = useM_PageOrganization_ProjectCreate();
mProjectCreate.mutation.mutate({ name: "New Project" });
Actions: Explicit suffix (no underscore): Create, Update, Delete, Upload
Folder: hooks/Page[Name]/useM_Page[Name]_[EntityAction].ts
// ✅ CORRECT - Never destructure
const sb_FromProjects_Insert = await supabase.from("projects").insert();
if (sb_FromProjects_Insert.error) throw sb_FromProjects_Insert.error;
return sb_FromProjects_Insert.data;
// ❌ WRONG
const { data, error } = await supabase.from("projects").insert();
import { useMutation } from "@tanstack/react-query";
import { supabase, type Supabase_InsertDto } from "@/configs/supabase/config";
import useApp from "antd/es/app/useApp";
export type UseM_PageOrganization_ProjectCreate_Params = Pick<
Supabase_InsertDto<"projects">,
"organization_id" | "name"
> &
Partial<Pick<Supabase_InsertDto<"projects">, "description">>;
export const useM_PageOrganization_ProjectCreate = () => {
const { message } = useApp();
const mutation = useMutation({
mutationFn: async (body: UseM_PageOrganization_ProjectCreate_Params) => {
const sb_FromProjects_Insert = await supabase
.from("projects")
.insert(body)
.select()
.single();
if (sb_FromProjects_Insert.error) throw sb_FromProjects_Insert.error;
return sb_FromProjects_Insert.data;
},
onSuccess: () => message.success("Project created successfully!"),
onError: (error) => {
console.error("Error creating project:", error);
message.error("Failed to create project!");
},
});
return { mutation };
};
Separates record ID (hook params) from mutable data (mutationFn params):
import type { AtLeastOne } from "@/types/utility.types";
export type UseM_PageOrganization_ProjectUpdate_Params = { projectId: string };
export type UseM_PageOrganization_ProjectUpdate_Body = AtLeastOne<
Pick<Supabase_UpdateDto<"projects">, "name" | "description">
>;
export const useM_PageOrganization_ProjectUpdate = ({
projectId,
}: UseM_PageOrganization_ProjectUpdate_Params) => {
const { message } = useApp();
const mutation = useMutation({
mutationKey: ["projects", "update", projectId],
mutationFn: async (body: UseM_PageOrganization_ProjectUpdate_Body) => {
const sb_FromProjects_Update = await supabase
.from("projects")
.update(body)
.eq("id", projectId)
.select()
.single();
if (sb_FromProjects_Update.error) throw sb_FromProjects_Update.error;
return sb_FromProjects_Update.data;
},
onSuccess: () => message.success("Project updated!"),
onError: (error) => {
console.error("Error updating project:", error);
message.error("Failed to update project!");
},
});
return { mutation };
};
| Aspect | Create | Update/Delete |
|---|---|---|
| Hook params | None () | Record ID ({ id }) |
| mutationFn params | Full body | Only mutable fields |
| Type exports | _Params only | _Params + _Body |
| mutationKey | Generic | Includes record ID |
const { modal } = App.useApp();
modal.confirm({
title: "Delete File",
content: `Delete "${fileName}"?`,
okType: "danger",
onOk: async () => await mFileDelete.mutation.mutateAsync({ fileId }),
});
mutateAsync: Use in modal.confirm() onOk, sequential operations
mutate: Normal button clicks, fire-and-forget
organizations, profiles): MUST manually invalidateonSuccess: () => {
queryClient.invalidateQueries({ queryKey: QueryKeys.organizations.all() });
},
// ❌ WRONG - logging twice
if (error) {
console.error(error);
throw error;
} // Logged here
onError: (error) => console.error(error); // And here!
// ✅ CORRECT - throw directly, let onError handle logging
if (error) throw error;
onError: (error) => {
console.error(error);
message.error("Failed!");
};
useM_[Scope]_[EntityAction]m[EntityAction]{ mutation } onlyonError onlyApp.useApp() for message({ entityId }), exports _Params + _Body, body uses AtLeastOnemPageOrganization_ProjectCreate → ✅ mProjectCreateUse when deploying Cloudflare Workers, managing R2 storage, or working with Cloudflare infrastructure
Use when working with ANTD components, theme tokens, icons, forms, or feedback components (message/notification/modal)
Use when adding, referencing, or serving static assets (images, fonts, videos, 3D models) through the R2 CDN pipeline with type-safe imports
Use when writing or reviewing JavaScript/TypeScript code for style patterns like concise arrows, inline handlers, expression formatting, or when tempted to use eslint-disable
Use when working with environment variables in frontend code
Use when creating or modifying keyboard shortcuts/hotkeys in frontend code