Skip to main content

mutations

Mutation render-prop component and mutationOptions for TanStack Query v4. Load when adding mutations inside list rows, loops, or conditionals where useMutation cannot be called, removing per-row wrapper components, or reusing mutation configs with mutationOptions.

معلومات المصدر

المستودع
toss/suspensive
آخر نشاط في المصدر
٤ يوليو ٢٠٢٦ في ٠٧:٠٤
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
١٬٠٥٩
التفرعات
٩٩

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
mutations
description
Mutation render-prop component and mutationOptions for TanStack Query v4. Load when adding mutations inside list rows, loops, or conditionals where useMutation cannot be called, removing per-row wrapper components, or reusing mutation configs with mutationOptions.
metadata
{"type":"sub-skill","library":"@suspensive/react-query-4","library_version":"3.21.2","framework":"react"}
requires
["react-query"]
sources
["toss/suspensive:docs/suspensive.org/src/content/en/docs/react-query/Mutation.mdx","toss/suspensive:docs/suspensive.org/src/content/en/docs/react-query/mutationOptions.mdx","toss/suspensive:docs/suspensive.org/src/content/en/docs/react-query/migration/migrate-to-v3.mdx"]
This skill builds on react-query. Read ../SKILL.md first. `<Mutation/>` exposes a useMutation result as a render prop so each list row or conditional branch can own its mutation without extracting a wrapper component. The render prop receives the full v4 UseMutationResult (`mutate`, `mutateAsync`, `isLoading`, `isError`, `error`, `data`, `reset`). Note the v4 naming: the in-flight flag is `isLoading`, not v5's `isPending`. ## Setup ```bash npm install @suspensive/react-query-4 @tanstack/react-query@4 ``` Import `Mutation` from '@suspensive/react-query-4'; import `mutationOptions` from '@tanstack/react-query' (official since TSQ 4.44+, which the package's peer range requires; the Suspensive re-export is deprecated). ## Core Patterns ### One mutation per list row ```tsx 'use client' import { Mutation } from '@suspensive/react-query-4' import { useSuspenseQuery } from '@tanstack/react-query' import { api } from '~/api' export const PostsPage = () => { const { data: posts } = useSuspenseQuery({ queryKey: ['posts'], queryFn: () => api.getPosts(), }) return posts.map((post) => ( <Mutation key={post.id} mutationFn={({ content }: { content: string }) => api.editPost({ postId: post.id, content })} > {(postMutation) => ( <div> {postMutation.isLoading ? <Spinner /> : null} <div>{post.content}</div> <textarea disabled={postMutation.isLoading} onBlur={(e) => postMutation.mutate({ content: e.target.value })} /> </div> )} </Mutation> )) } ``` ### Reusable configs with mutationOptions ```tsx 'use client' import { Mutation } from '@suspensive/react-query-4' import { mutationOptions, useMutation } from '@tanstack/react-query' const editPostMutationOptions = (postId: number) => mutationOptions({ mutationFn: (content: string) => fetch(`/api/posts/${postId}`, { method: 'PATCH', body: JSON.stringify({ content }), }).then((res) => res.json()), }) // Same options work with the hook... const useEditPost = (postId: number) => useMutation(editPostMutationOptions(postId)) // ...and spread directly into the component export const EditPost = ({ postId }: { postId: number }) => ( <Mutation {...editPostMutationOptions(postId)}> {({ mutate, isLoading }) => ( <div> <p>{isLoading ? 'Updating...' : 'Latest updated'}</p> <textarea onBlur={(e) => mutate(e.target.value)} disabled={isLoading} /> </div> )} </Mutation> ) ``` ### Invalidating queries on success ```tsx 'use client' import { Mutation } from '@suspensive/react-query-4' import { useQueryClient } from '@tanstack/react-query' import { api } from '~/api' export const DeletePostButton = ({ postId }: { postId: number }) => { const queryClient = useQueryClient() return ( <Mutation mutationFn={() => api.deletePost(postId)} onSuccess={() => queryClient.invalidateQueries({ queryKey: ['posts'] })} > {({ mutate, isLoading }) => ( <button disabled={isLoading} onClick={() => mutate()}> {isLoading ? 'Deleting...' : 'Delete'} </button> )} </Mutation> ) } ``` ## Common Mistakes ### [MEDIUM] Wrapper component per row just for useMutation Wrong: ```tsx { posts.map((post) => <PostItemWithMutation key={post.id} post={post} />) } const PostItemWithMutation = ({ post }: { post: Post }) => { const deleteMutation = useMutation({ mutationFn: () => api.deletePost(post.id) }) return ( <button disabled={deleteMutation.isLoading} onClick={() => deleteMutation.mutate()}> delete </button> ) } ``` Correct: ```tsx { posts.map((post) => ( <Mutation key={post.id} mutationFn={() => api.deletePost(post.id)}> {({ mutate, isLoading }) => ( <button disabled={isLoading} onClick={() => mutate()}> delete </button> )} </Mutation> )) } ``` Hooks cannot be called in loops, so agents invent awkwardly named wrapper components; `<Mutation>` works inline per row with no extra depth. Source: docs/suspensive.org/src/content/en/docs/react-query/Mutation.mdx ### [MEDIUM] Using v5 isPending on v4 mutation results Wrong: ```tsx <Mutation mutationFn={() => api.deletePost(postId)}> {({ mutate, isPending }) => ( <button disabled={isPending} onClick={() => mutate()}> delete </button> )} </Mutation> ``` Correct: ```tsx <Mutation mutationFn={() => api.deletePost(postId)}> {({ mutate, isLoading }) => ( <button disabled={isLoading} onClick={() => mutate()}> delete </button> )} </Mutation> ``` The render prop receives TSQ v4's UseMutationResult, where the in-flight flag is `isLoading`; `isPending` only exists in TSQ v5 (and @suspensive/react-query-5). Source: docs/suspensive.org/src/content/en/docs/react-query/Mutation.mdx ### [MEDIUM] Importing mutationOptions from Suspensive Wrong: ```tsx import { mutationOptions } from '@suspensive/react-query-4' ``` Correct: ```tsx import { mutationOptions } from '@tanstack/react-query' ``` mutationOptions is official in TanStack Query since v4.44 (the package's peer range requires ^4.44.0); the Suspensive re-export exists only for compatibility and is deprecated. Source: docs/suspensive.org/src/content/en/docs/react-query/mutationOptions.mdx See also: ../declarative-queries/SKILL.md for the matching SuspenseQuery pattern, ../SKILL.md for which imports come from @tanstack/react-query vs Suspensive.
عرض على GitHub