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.

Quellinformationen

Repository
toss/suspensive
Letzte Quellaktivität
4. Juli 2026 um 07:04
Erkannte Sprache von SKILL.md
Englisch
Sterne
1.059
Forks
99

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
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.
Auf GitHub ansehen