Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Copy link

Choose a reason for hiding this comment

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

Incorrect XML Tag in OffsetSymbol Serialization

XML tag uses 'start_sym' instead of 'offset_sym' for OffsetSymbol serialization. This creates incorrect XML structure causing deserialization failures when loading symbol tables and compromises system reliability through symbol table corruption during persistence operations.

Standards
  • ISO-IEC-25010-Reliability-Fault-Tolerance
  • ISO-IEC-25010-Functional-Correctness-Appropriateness

mutationOptions({
Copy link

Choose a reason for hiding this comment

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

Incorrect XML Tag in OffsetSymbol Serialization

XML tag uses 'start_sym' instead of 'offset_sym' for OffsetSymbol serialization. This creates incorrect XML structure causing deserialization failures when loading symbol tables and compromises system reliability through symbol table corruption during persistence operations.

Standards
  • ISO-IEC-25010-Reliability-Fault-Tolerance
  • ISO-IEC-25010-Functional-Correctness-Appropriateness

Copy link

Choose a reason for hiding this comment

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

Incorrect XML Tag in OffsetSymbol Serialization

XML tag uses 'start_sym' instead of 'offset_sym' for OffsetSymbol serialization. This creates incorrect XML structure causing deserialization failures when loading symbol tables and compromises system reliability through symbol table corruption during persistence operations.

Standards
  • ISO-IEC-25010-Reliability-Fault-Tolerance
  • ISO-IEC-25010-Functional-Correctness-Appropriateness

Comment on lines +5 to +7
Copy link

Choose a reason for hiding this comment

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

Incorrect XML Tag in OffsetSymbol Serialization

XML tag uses 'start_sym' instead of 'offset_sym' for OffsetSymbol serialization. This creates incorrect XML structure causing deserialization failures when loading symbol tables and compromises system reliability through symbol table corruption during persistence operations.

Standards
  • ISO-IEC-25010-Reliability-Fault-Tolerance
  • ISO-IEC-25010-Functional-Correctness-Appropriateness

Comment on lines +5 to +7
Copy link

Choose a reason for hiding this comment

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

Incorrect XML Tag in OffsetSymbol Serialization

XML tag uses 'start_sym' instead of 'offset_sym' for OffsetSymbol serialization. This creates incorrect XML structure causing deserialization failures when loading symbol tables and compromises system reliability through symbol table corruption during persistence operations.

Standards
  • ISO-IEC-25010-Reliability-Fault-Tolerance
  • ISO-IEC-25010-Functional-Correctness-Appropriateness

mutationFn,
Copy link

Choose a reason for hiding this comment

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

Incorrect XML Tag in OffsetSymbol Serialization

XML tag uses 'start_sym' instead of 'offset_sym' for OffsetSymbol serialization. This creates incorrect XML structure causing deserialization failures when loading symbol tables and compromises system reliability through symbol table corruption during persistence operations.

Standards
  • ISO-IEC-25010-Reliability-Fault-Tolerance
  • ISO-IEC-25010-Functional-Correctness-Appropriateness

Copy link

Choose a reason for hiding this comment

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

Incorrect XML Tag in OffsetSymbol Serialization

XML tag uses 'start_sym' instead of 'offset_sym' for OffsetSymbol serialization. This creates incorrect XML structure causing deserialization failures when loading symbol tables and compromises system reliability through symbol table corruption during persistence operations.

Standards
  • ISO-IEC-25010-Reliability-Fault-Tolerance
  • ISO-IEC-25010-Functional-Correctness-Appropriateness

Comment on lines +7 to +8
Copy link

Choose a reason for hiding this comment

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

Incorrect XML Tag in OffsetSymbol Serialization

XML tag uses 'start_sym' instead of 'offset_sym' for OffsetSymbol serialization. This creates incorrect XML structure causing deserialization failures when loading symbol tables and compromises system reliability through symbol table corruption during persistence operations.

Standards
  • ISO-IEC-25010-Reliability-Fault-Tolerance
  • ISO-IEC-25010-Functional-Correctness-Appropriateness

...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() {

Choose a reason for hiding this comment

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

medium

The function name useGroupPostMutation can be misleading. Typically, a hook named use...Mutation would return the result of useMutation (e.g., mutate, isPending, etc.). This function, however, is a factory that returns options for useMutation.

To improve clarity and align with conventions, consider renaming it to useGroupPostMutationOptions. This makes it clear that the hook provides options rather than being the mutation hook itself.

Suggested change
function useGroupPostMutation() {
function useGroupPostMutationOptions() {

const queryClient = useQueryClient()

return mutationOptions({
mutationKey: ['groups'],
mutationFn: executeGroups,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['posts'] })
Comment on lines +248 to +254
Copy link

Choose a reason for hiding this comment

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

Incorrect Return Type

Function useGroupPostMutation returns mutationOptions result but should return useMutation hook. Documentation example shows incorrect pattern that would not work as a React hook. Business logic violation: hook must return mutation object with mutate function, not just options configuration.

Standards
  • Algorithm-Correctness-Return-Logic
  • Business-Rule-Hook-Pattern
  • Logic-Verification-React-Hooks

},
})
}
Comment on lines +247 to +257
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix the hook naming convention.

The function useGroupPostMutation() uses the "use" prefix, which by React convention indicates it's a hook. However, it only returns mutation options without calling useMutation, violating this convention.

Apply one of these fixes:

Option 1 (recommended): Remove the "use" prefix to match the queryOptions pattern above:

-function useGroupPostMutation() {
+function groupPostMutationOptions() {
   const queryClient = useQueryClient()
 
   return mutationOptions({
     mutationKey: ['groups'],
     mutationFn: executeGroups,
     onSuccess: () => {
       queryClient.invalidateQueries({ queryKey: ['posts'] })
     },
   })
 }
+
+// Usage:
+useMutation(groupPostMutationOptions())

Option 2: Make it a proper hook:

 function useGroupPostMutation() {
   const queryClient = useQueryClient()
 
-  return mutationOptions({
+  return useMutation(mutationOptions({
     mutationKey: ['groups'],
     mutationFn: executeGroups,
     onSuccess: () => {
       queryClient.invalidateQueries({ queryKey: ['posts'] })
     },
-  })
+  }))
 }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In docs/framework/react/typescript.md around lines 247 to 257, the function is
named useGroupPostMutation but only returns mutation options (not a React hook),
so rename it to remove the "use" prefix to match the existing queryOptions
pattern (e.g., groupPostMutationOptions or groupPostMutation) OR alternatively
convert it into a real hook by calling useMutation with the mutation options and
returning the mutation result; update all references accordingly to the new
name/usage.

```

## 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
51 changes: 51 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,51 @@
import { describe, expectTypeOf, it } from 'vitest'
import { dataTagSymbol } from '@tanstack/query-core'
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>()
},
})
})

it('should tag the mutationKey with the result type of the MutationFn', () => {
const { mutationKey } = mutationOptions({
mutationKey: ['key'],
mutationFn: () => Promise.resolve(5),
})

expectTypeOf(mutationKey[dataTagSymbol]).toEqualTypeOf<number>()
})

it('should tag the mutationKey with unknown if there is no mutationFn', () => {
const { mutationKey } = mutationOptions({
mutationKey: ['key'],
})

expectTypeOf(mutationKey[dataTagSymbol]).toEqualTypeOf<unknown>()
})

it('should tag the mutationKey with the result type of the MutationFn if onSuccess is used', () => {
const { mutationKey } = mutationOptions({
mutationKey: ['key'],
mutationFn: () => Promise.resolve(5),
onSuccess: () => {},
})

expectTypeOf(mutationKey[dataTagSymbol]).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)
})
})
105 changes: 105 additions & 0 deletions packages/react-query/src/mutationOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import type {
DataTag,
DefaultError,
InitialDataFunction,
MutationFunction,
OmitKeyof,
SkipToken,
} from '@tanstack/query-core'
import type { UseMutationOptions } from './types'

export type UndefinedInitialDataOptions<
TMutationFnData = unknown,
TError = DefaultError,
TData = void,
TMutationKey = unknown,
> = UseMutationOptions<TMutationFnData, TError, TData, TMutationKey> & {
initialData?:
| undefined
| InitialDataFunction<NonUndefinedGuard<TMutationFnData>>
| NonUndefinedGuard<TMutationFnData>
}

export type UnusedSkipTokenOptions<
TMutationFnData = unknown,
TError = DefaultError,
TData = void,
TMutationKey = unknown,
> = OmitKeyof<
UseMutationOptions<TMutationFnData, TError, TData, TMutationKey>,
'mutationFn'
> & {
mutationFn?: Exclude<
UseMutationOptions<
TMutationFnData,
TError,
TData,
TMutationKey
>['mutationFn'],
SkipToken | undefined
>
}

type NonUndefinedGuard<T> = T extends undefined ? never : T
Copy link

Choose a reason for hiding this comment

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

Type Constraint Logic

NonUndefinedGuard type logic excludes undefined but allows null values to pass through. Type constraint incomplete for proper data validation. Mathematical logic flaw: constraint should handle both undefined and null for complete type safety.

Standards
  • Algorithm-Correctness-Type-Logic
  • Mathematical-Accuracy-Set-Theory
  • Logic-Verification-Type-Safety


export type DefinedInitialDataOptions<
TMutationFnData = unknown,
TError = DefaultError,
TData = void,
TMutationKey = unknown,
> = Omit<
UseMutationOptions<TMutationFnData, TError, TData, TMutationKey>,
'mutationFn'
> & {
initialData:
| NonUndefinedGuard<TMutationFnData>
| (() => NonUndefinedGuard<TMutationFnData>)
mutationFn?: MutationFunction<TMutationFnData, TMutationKey>
}

export function mutationOptions<
TMutationFnData = unknown,
TError = DefaultError,
TData = void,
TMutationKey = unknown,
>(
options: DefinedInitialDataOptions<
TMutationFnData,
TError,
TData,
TMutationKey
>,
): DefinedInitialDataOptions<TMutationFnData, TError, TData, TMutationKey> & {
mutationKey: DataTag<TMutationKey, TMutationFnData, TError>
}
Comment on lines +60 to +74
Copy link

Choose a reason for hiding this comment

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

Complex Type Overloads

Multiple function overloads with complex generic type parameters create high cognitive load and maintenance burden. Three separate overloads handling different option types increases complexity for developers understanding the API. Consider consolidating overloads or extracting type resolution logic to reduce interface complexity.

Standards
  • Clean-Code-Functions
  • SOLID-ISP
  • Maintainability-Quality-Complexity


export function mutationOptions<
TMutationFnData = unknown,
TError = DefaultError,
TData = void,
TMutationKey = unknown,
>(
options: UnusedSkipTokenOptions<TMutationFnData, TError, TData, TMutationKey>,
): UnusedSkipTokenOptions<TMutationFnData, TError, TData, TMutationKey> & {
mutationKey: DataTag<TMutationKey, TMutationFnData, TError>
}

export function mutationOptions<
TMutationFnData = unknown,
TError = DefaultError,
TData = void,
TMutationKey = unknown,
>(
options: UndefinedInitialDataOptions<
TMutationFnData,
TError,
TData,
TMutationKey
>,
): UndefinedInitialDataOptions<TMutationFnData, TError, TData, TMutationKey> & {
mutationKey: DataTag<TMutationKey, TMutationFnData, TError>
}

export function mutationOptions(options: unknown) {
return options
}
Comment on lines +1 to +105

Choose a reason for hiding this comment

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

critical

This file seems to be a direct copy of queryOptions.ts and contains concepts that are not applicable to mutations, leading to incorrect typings.

  1. initialData: Mutations do not support initialData. This is a concept for queries. The types UndefinedInitialDataOptions and DefinedInitialDataOptions, and their corresponding mutationOptions overloads, should be removed.
  2. skipToken: skipToken is used for disabling queries and is not applicable to mutations, which are triggered imperatively. The UnusedSkipTokenOptions type and its overload should be removed.
  3. Incorrect Generics: The generics are incorrectly passed to UseMutationOptions. For example, TMutationKey is passed as the TContext generic, and TData is passed as the TVariables generic. This is incorrect and will lead to type errors and confusion.

The file should be rewritten to correctly type mutationOptions for mutations, likely with a much simpler structure that focuses on inferring types from mutationFn without the query-specific concepts.

Loading