Skip to content

feat(query-core): add MutationFunctionContext argument to mutateFn #9193

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 2 commits into
base: main
Choose a base branch
from
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
3 changes: 2 additions & 1 deletion docs/framework/react/reference/useMutation.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,11 @@ mutate(variables, {

**Parameter1 (Options)**

- `mutationFn: (variables: TVariables) => Promise<TData>`
- `mutationFn: (variables: TVariables, context: MutationFunctionContext) => Promise<TData>`
- **Required, but only if no default mutation function has been defined**
- A function that performs an asynchronous task and returns a promise.
- `variables` is an object that `mutate` will pass to your `mutationFn`
- `context` is an object that `mutate` will pass to your `mutationFn`. Contains reference to `QueryClient`, `mutationKey` and optional `meta` object.
- `gcTime: number | Infinity`
- The time in milliseconds that unused/inactive cache data remains in memory. When a mutation's cache becomes unused or inactive, that cache data will be garbage collected after this duration. When different cache times are specified, the longest one will be used.
- If set to `Infinity`, will disable garbage collection
Expand Down
31 changes: 30 additions & 1 deletion packages/query-core/src/__tests__/mutations.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,37 @@ describe('mutations', () => {
'vars',
)

const args = fn.mock.calls[0]!

expect(fn).toHaveBeenCalledTimes(1)
expect(args[0]).toEqual('vars')
})

test('should provide MutationFunctionContext to mutateFn', async () => {
const key = queryKey()
const fn = vi.fn()
const meta = { hello: 'world' }

await executeMutation(
queryClient,
{
mutationKey: key,
mutationFn: fn,
meta: meta,
},
'vars',
)

const args = fn.mock.calls[0]!
expect(fn).toHaveBeenCalledTimes(1)
expect(fn).toHaveBeenCalledWith('vars')
expect(args).toBeDefined()

const vars = args[0]
const mutationFnContext = args[1]
expect(vars).toEqual('vars')
expect(mutationFnContext.client).toEqual(queryClient)
expect(mutationFnContext.meta).toEqual(meta)
expect(mutationFnContext.mutationKey).toEqual(key)
})

test('mutation should set correct success states', async () => {
Expand Down
1 change: 1 addition & 0 deletions packages/query-core/src/__tests__/utils.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,7 @@ describe('core/utils', () => {
const filters = { mutationKey: ['key1'] }
const queryClient = new QueryClient()
const mutation = new Mutation({
client: queryClient,
mutationId: 1,
mutationCache: queryClient.getMutationCache(),
options: {},
Expand Down
14 changes: 13 additions & 1 deletion packages/query-core/src/mutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,20 @@ import { Removable } from './removable'
import { createRetryer } from './retryer'
import type {
DefaultError,
MutationFunctionContext,
MutationMeta,
MutationOptions,
MutationStatus,
} from './types'
import type { MutationCache } from './mutationCache'
import type { MutationObserver } from './mutationObserver'
import type { Retryer } from './retryer'
import type { QueryClient } from './queryClient'

// TYPES

interface MutationConfig<TData, TError, TVariables, TContext> {
client: QueryClient
mutationId: number
mutationCache: MutationCache
options: MutationOptions<TData, TError, TVariables, TContext>
Expand Down Expand Up @@ -88,13 +91,15 @@ export class Mutation<
options!: MutationOptions<TData, TError, TVariables, TContext>
readonly mutationId: number

#client: QueryClient
#observers: Array<MutationObserver<TData, TError, TVariables, TContext>>
#mutationCache: MutationCache
#retryer?: Retryer<TData>

constructor(config: MutationConfig<TData, TError, TVariables, TContext>) {
super()

this.#client = config.client
this.mutationId = config.mutationId
this.#mutationCache = config.mutationCache
this.#observers = []
Expand Down Expand Up @@ -171,7 +176,14 @@ export class Mutation<
if (!this.options.mutationFn) {
return Promise.reject(new Error('No mutationFn found'))
}
return this.options.mutationFn(variables)

const mutationFnContext: MutationFunctionContext = {
client: this.#client,
meta: this.options.meta,
mutationKey: this.options.mutationKey,
}

return this.options.mutationFn(variables, mutationFnContext)
},
onFail: (failureCount, error) => {
this.#dispatch({ type: 'failed', failureCount, error })
Expand Down
1 change: 1 addition & 0 deletions packages/query-core/src/mutationCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ export class MutationCache extends Subscribable<MutationCacheListener> {
state?: MutationState<TData, TError, TVariables, TContext>,
): Mutation<TData, TError, TVariables, TContext> {
const mutation = new Mutation({
client: client,
mutationCache: this,
mutationId: ++this.#mutationId,
options: client.defaultMutationOptions(options),
Expand Down
7 changes: 7 additions & 0 deletions packages/query-core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1086,8 +1086,15 @@ export type MutationMeta = Register extends {
: Record<string, unknown>
: Record<string, unknown>

export type MutationFunctionContext = {
client: QueryClient
meta: MutationMeta | undefined
mutationKey?: MutationKey
}

export type MutationFunction<TData = unknown, TVariables = unknown> = (
variables: TVariables,
mutationFnContext: MutationFunctionContext,
) => Promise<TData>

export interface MutationOptions<
Expand Down