Skip to content

feat(react-query): add mutationOptions #8960

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 25 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
596896d
feat(react-query): add mutationOptions
Ubinquitous Apr 6, 2025
ff15e5d
test(react-query): add DataTag test case
Ubinquitous Apr 7, 2025
ea54b58
Merge branch 'main' into feature/react-query-mutation-options
TkDodo May 1, 2025
2972edd
fix(react-query): remove unnecessary types from mutation
Ubinquitous May 1, 2025
08a5026
fix(react-query): remove unncessary type overload
Ubinquitous May 1, 2025
f3b74c0
Merge branch 'main' into feature/react-query-mutation-options
manudeli May 1, 2025
a4560d3
Merge branch 'main' into feature/react-query-mutation-options
manudeli May 5, 2025
6889638
chore(react-query): add mutationOptions to barrel file
Ubinquitous May 5, 2025
b844dee
Merge branch 'main' into feature/react-query-mutation-options
manudeli May 6, 2025
e61227d
Merge branch 'main' into feature/react-query-mutation-options
manudeli May 6, 2025
33d3e9f
fix(react-query): fix test eslint issue
Ubinquitous May 7, 2025
fd7b9f9
docs(react-query): add more examples
Ubinquitous May 7, 2025
6ee8c76
Merge branch 'main' into feature/react-query-mutation-options
manudeli May 7, 2025
299a19f
Merge branch 'main' into feature/react-query-mutation-options
manudeli May 9, 2025
2622902
Merge branch 'main' into feature/react-query-mutation-options
TkDodo May 13, 2025
9ded37d
test(react-query): add more test cases
Ubinquitous May 20, 2025
48d867b
chore(react-query): Change mutaitonKey to required
Ubinquitous Jun 6, 2025
05f4fc0
fix(react-query): fix test code type error
Ubinquitous Jun 6, 2025
b202d6e
test(react-query): add testcase when used with other mutation util
Ubinquitous Jun 7, 2025
167fb8c
fix(react-query): fix error test code and avoid use deprecateed method
Ubinquitous Jun 7, 2025
2b85c72
fix(react-query): fix error test code and avoid use deprecateed method
Ubinquitous Jun 7, 2025
df3545a
fix(react-query): fix import detect error
Ubinquitous Jun 7, 2025
08769ac
fix(react-query): fix import detect error
Ubinquitous Jun 7, 2025
36a8af1
fix(react-query): add function overload
Ubinquitous Jun 10, 2025
22f5ed2
test(react-query): fix mutation options test code
Ubinquitous Jun 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions docs/framework/react/reference/mutationOptions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
id: mutationOptions
title: mutationOptions
---

```tsx
mutationOptions({
mutationFn,
...options,
})
```

**Options**

You can generally pass everything to `mutationOptions` that you can also pass to [`useMutation`](./useMutation.md).
18 changes: 18 additions & 0 deletions docs/framework/react/typescript.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,24 @@ const data = queryClient.getQueryData<Group[]>(['groups'])
[//]: # 'TypingQueryOptions'
[//]: # 'Materials'

## Typing Mutation Options

Similarly to `queryOptions`, you can use `mutationOptions` to extract mutation options into a separate function:

```ts
function useGroupPostMutation() {
const queryClient = useQueryClient()

return mutationOptions({
mutationKey: ['groups'],
mutationFn: executeGroups,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['posts'] })
},
})
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be worth highlighting in the documentation that mutationOptions can be reused across different interfaces—such as useMutation, useIsMutating, and queryClient.isMutating.

Suggested change
function useGroupPostMutation() {
const queryClient = useQueryClient()
return mutationOptions({
mutationKey: ['groups'],
mutationFn: executeGroups,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['posts'] })
},
})
}
function groupMutationOptions() {
return mutationOptions({
mutationKey: ['groups'],
mutationFn: addGroup,
})
}
useMutation({
...groupMutationOptions()
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['groups'] })
})
useIsMutating(groupMutationOptions())
queryClient.isMutating(groupMutationOptions())

```

## Further Reading

For tips and tricks around type inference, have a look at [React Query and TypeScript](./community/tkdodos-blog.md#6-react-query-and-typescript) from
Expand Down
23 changes: 23 additions & 0 deletions packages/react-query/src/__tests__/mutationOptions.test-d.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { describe, expectTypeOf, it } from 'vitest'
import { mutationOptions } from '../mutationOptions'

describe('mutationOptions', () => {
it('should not allow excess properties', () => {
return mutationOptions({
mutationFn: () => Promise.resolve(5),
mutationKey: ['key'],
// @ts-expect-error this is a good error, because onMutates does not exist!
onMutates: 1000,
})
})

it('should infer types for callbacks', () => {
return mutationOptions({
mutationFn: () => Promise.resolve(5),
mutationKey: ['key'],
onSuccess: (data) => {
expectTypeOf(data).toEqualTypeOf<number>()
},
})
})
})
14 changes: 14 additions & 0 deletions packages/react-query/src/__tests__/mutationOptions.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { describe, expect, it } from 'vitest'
import { mutationOptions } from '../mutationOptions'
import type { UseMutationOptions } from '../types'

describe('mutationOptions', () => {
it('should return the object received as a parameter without any modification.', () => {
const object: UseMutationOptions = {
mutationKey: ['key'],
mutationFn: () => Promise.resolve(5),
} as const

expect(mutationOptions(object)).toStrictEqual(object)
})
})
15 changes: 15 additions & 0 deletions packages/react-query/src/mutationOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { DefaultError } from '@tanstack/query-core'
import type { UseMutationOptions } from './types'

export function mutationOptions<
TData = unknown,
TError = DefaultError,
TVariables = void,
TContext = unknown,
>(
options: UseMutationOptions<TData, TError, TVariables, TContext>,
): UseMutationOptions<TData, TError, TVariables, TContext>

export function mutationOptions(options: unknown) {
return options
}
Loading