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.

Informations de source

Dépôt
toss/suspensive
Dernière activité de la source
4 juillet 2026 à 07:04
Langue détectée de SKILL.md
anglais
Étoiles
1 059
Forks
99

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
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.
Voir sur GitHub