Skip to content

Commit 596896d

Browse files
committed
feat(react-query): add mutationOptions
1 parent 8e0c6b7 commit 596896d

File tree

5 files changed

+175
-0
lines changed

5 files changed

+175
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
id: mutationOptions
3+
title: mutationOptions
4+
---
5+
6+
```tsx
7+
mutationOptions({
8+
mutationFn,
9+
...options,
10+
})
11+
```
12+
13+
**Options**
14+
15+
You can generally pass everything to `mutationOptions` that you can also pass to [`useMutation`](./useMutation.md).

docs/framework/react/typescript.md

+18
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,24 @@ const data = queryClient.getQueryData<Group[]>(['groups'])
239239
[//]: # 'TypingQueryOptions'
240240
[//]: # 'Materials'
241241

242+
## Typing Mutation Options
243+
244+
Similarly to `queryOptions`, you can use `mutationOptions` to extract mutation options into a separate function:
245+
246+
```ts
247+
function useGroupPostMutation() {
248+
const queryClient = useQueryClient()
249+
250+
return mutationOptions({
251+
mutationKey: ['groups'],
252+
mutationFn: executeGroups,
253+
onSuccess: () => {
254+
queryClient.invalidateQueries({ queryKey: ['posts'] })
255+
},
256+
})
257+
}
258+
```
259+
242260
## Further Reading
243261

244262
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
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { describe, expectTypeOf, it } from 'vitest'
2+
import { mutationOptions } from '../mutationOptions'
3+
4+
describe('mutationOptions', () => {
5+
it('should not allow excess properties', () => {
6+
return mutationOptions({
7+
mutationFn: () => Promise.resolve(5),
8+
mutationKey: ['key'],
9+
// @ts-expect-error this is a good error, because onMutates does not exist!
10+
onMutates: 1000,
11+
})
12+
})
13+
14+
it('should infer types for callbacks', () => {
15+
return mutationOptions({
16+
mutationFn: () => Promise.resolve(5),
17+
mutationKey: ['key'],
18+
onSuccess: (data) => {
19+
expectTypeOf(data).toEqualTypeOf<number>()
20+
},
21+
})
22+
})
23+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { describe, expect, it } from 'vitest'
2+
import { mutationOptions } from '../mutationOptions'
3+
import type { UseMutationOptions } from '../types'
4+
5+
describe('mutationOptions', () => {
6+
it('should return the object received as a parameter without any modification.', () => {
7+
const object: UseMutationOptions = {
8+
mutationKey: ['key'],
9+
mutationFn: () => Promise.resolve(5),
10+
} as const
11+
12+
expect(mutationOptions(object)).toStrictEqual(object)
13+
})
14+
})
+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import type {
2+
DataTag,
3+
DefaultError,
4+
InitialDataFunction,
5+
MutationFunction,
6+
OmitKeyof,
7+
SkipToken,
8+
} from '@tanstack/query-core'
9+
import type { UseMutationOptions } from './types'
10+
11+
export type UndefinedInitialDataOptions<
12+
TMutationFnData = unknown,
13+
TError = DefaultError,
14+
TData = void,
15+
TMutationKey = unknown,
16+
> = UseMutationOptions<TMutationFnData, TError, TData, TMutationKey> & {
17+
initialData?:
18+
| undefined
19+
| InitialDataFunction<NonUndefinedGuard<TMutationFnData>>
20+
| NonUndefinedGuard<TMutationFnData>
21+
}
22+
23+
export type UnusedSkipTokenOptions<
24+
TMutationFnData = unknown,
25+
TError = DefaultError,
26+
TData = void,
27+
TMutationKey = unknown,
28+
> = OmitKeyof<
29+
UseMutationOptions<TMutationFnData, TError, TData, TMutationKey>,
30+
'mutationFn'
31+
> & {
32+
mutationFn?: Exclude<
33+
UseMutationOptions<
34+
TMutationFnData,
35+
TError,
36+
TData,
37+
TMutationKey
38+
>['mutationFn'],
39+
SkipToken | undefined
40+
>
41+
}
42+
43+
type NonUndefinedGuard<T> = T extends undefined ? never : T
44+
45+
export type DefinedInitialDataOptions<
46+
TMutationFnData = unknown,
47+
TError = DefaultError,
48+
TData = void,
49+
TMutationKey = unknown,
50+
> = Omit<
51+
UseMutationOptions<TMutationFnData, TError, TData, TMutationKey>,
52+
'mutationFn'
53+
> & {
54+
initialData:
55+
| NonUndefinedGuard<TMutationFnData>
56+
| (() => NonUndefinedGuard<TMutationFnData>)
57+
mutationFn?: MutationFunction<TMutationFnData, TMutationKey>
58+
}
59+
60+
export function mutationOptions<
61+
TMutationFnData = unknown,
62+
TError = DefaultError,
63+
TData = void,
64+
TMutationKey = unknown,
65+
>(
66+
options: DefinedInitialDataOptions<
67+
TMutationFnData,
68+
TError,
69+
TData,
70+
TMutationKey
71+
>,
72+
): DefinedInitialDataOptions<TMutationFnData, TError, TData, TMutationKey> & {
73+
mutationKey: DataTag<TMutationKey, TMutationFnData, TError>
74+
}
75+
76+
export function mutationOptions<
77+
TMutationFnData = unknown,
78+
TError = DefaultError,
79+
TData = void,
80+
TMutationKey = unknown,
81+
>(
82+
options: UnusedSkipTokenOptions<TMutationFnData, TError, TData, TMutationKey>,
83+
): UnusedSkipTokenOptions<TMutationFnData, TError, TData, TMutationKey> & {
84+
mutationKey: DataTag<TMutationKey, TMutationFnData, TError>
85+
}
86+
87+
export function mutationOptions<
88+
TMutationFnData = unknown,
89+
TError = DefaultError,
90+
TData = void,
91+
TMutationKey = unknown,
92+
>(
93+
options: UndefinedInitialDataOptions<
94+
TMutationFnData,
95+
TError,
96+
TData,
97+
TMutationKey
98+
>,
99+
): UndefinedInitialDataOptions<TMutationFnData, TError, TData, TMutationKey> & {
100+
mutationKey: DataTag<TMutationKey, TMutationFnData, TError>
101+
}
102+
103+
export function mutationOptions(options: unknown) {
104+
return options
105+
}

0 commit comments

Comments
 (0)