Skip to content
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

Add context as second parameter for the mutation function #7343

Open
wants to merge 3 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/framework/react/reference/useMutation.md
Expand Up @@ -45,10 +45,11 @@ mutate(variables, {

**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 containing the `mutationKey` and the mutation's `meta`
- `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
27 changes: 25 additions & 2 deletions packages/query-core/src/__tests__/mutations.test.tsx
Expand Up @@ -2,7 +2,7 @@ import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
import { waitFor } from '@testing-library/react'
import { MutationObserver } from '../mutationObserver'
import { createQueryClient, executeMutation, queryKey, sleep } from './utils'
import type { QueryClient } from '..'
import type { MutationFunctionContext, QueryClient } from '..'
import type { MutationState } from '../mutation'

describe('mutations', () => {
Expand Down Expand Up @@ -49,7 +49,30 @@ describe('mutations', () => {
)

expect(fn).toHaveBeenCalledTimes(1)
expect(fn).toHaveBeenCalledWith('vars')
expect(fn).toHaveBeenCalledWith('vars', expect.anything())
})

test('should provide context to mutationFn', async () => {
const mutationKey = queryKey()
const vars = 'vars' as const
const meta = { a: 1 }
const mutationFn = vi
.fn<[typeof vars, MutationFunctionContext], Promise<'data'>>()
.mockResolvedValue('data')

const mutation = new MutationObserver(queryClient, {
mutationKey,
mutationFn,
meta,
})

await mutation.mutate(vars)

expect(mutationFn).toHaveBeenCalledTimes(1)
const context = mutationFn.mock.calls[0]![1]
expect(context).toBeDefined()
expect(context.mutationKey).toEqual(mutationKey)
expect(context.meta).toEqual(meta)
})

test('mutation should set correct success states', async () => {
Expand Down
7 changes: 6 additions & 1 deletion packages/query-core/src/mutation.ts
Expand Up @@ -3,6 +3,7 @@ import { Removable } from './removable'
import { createRetryer } from './retryer'
import type {
DefaultError,
MutationFunctionContext,
MutationMeta,
MutationOptions,
MutationStatus,
Expand Down Expand Up @@ -167,7 +168,11 @@ export class Mutation<
if (!this.options.mutationFn) {
return Promise.reject(new Error('No mutationFn found'))
}
return this.options.mutationFn(variables)
const mutationFnContext: MutationFunctionContext = {
mutationKey: this.options.mutationKey,
meta: this.meta,
}
return this.options.mutationFn(variables, mutationFnContext)
},
onFail: (failureCount, error) => {
this.#dispatch({ type: 'failed', failureCount, error })
Expand Down
6 changes: 6 additions & 0 deletions packages/query-core/src/types.ts
Expand Up @@ -888,8 +888,14 @@ export type MutationMeta = Register extends {
: Record<string, unknown>
: Record<string, unknown>

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

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

export interface MutationOptions<
Expand Down