Skip to content

Commit 5285479

Browse files
feat(useMutation): meta for mutations (TanStack#2906)
1 parent 62e12e5 commit 5285479

File tree

6 files changed

+78
-1
lines changed

6 files changed

+78
-1
lines changed

docs/src/pages/reference/useMutation.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const {
2323
onSettled,
2424
onSuccess,
2525
useErrorBoundary,
26+
meta,
2627
})
2728

2829
mutate(variables, {
@@ -71,6 +72,9 @@ mutate(variables, {
7172
- Set this to `true` if you want mutation errors to be thrown in the render phase and propagate to the nearest error boundary
7273
- Set this to `false` to disable the behavior of throwing errors to the error boundary.
7374
- If set to a function, it will be passed the error and should return a boolean indicating whether to show the error in an error boundary (`true`) or return the error as state (`false`)
75+
- `meta: Record<string, unknown>`
76+
- Optional
77+
- If set, stores additional information on the mutation cache entry that can be used as needed. It will be accessible wherever the `mutation` is available (eg. `onError`, `onSuccess` functions of the `MutationCache`).
7478

7579
**Returns**
7680

src/core/mutation.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { MutationOptions, MutationStatus } from './types'
1+
import type { MutationOptions, MutationStatus, MutationMeta } from './types'
22
import type { MutationCache } from './mutationCache'
33
import type { MutationObserver } from './mutationObserver'
44
import { getLogger } from './logger'
@@ -14,6 +14,7 @@ interface MutationConfig<TData, TError, TVariables, TContext> {
1414
options: MutationOptions<TData, TError, TVariables, TContext>
1515
defaultOptions?: MutationOptions<TData, TError, TVariables, TContext>
1616
state?: MutationState<TData, TError, TVariables, TContext>
17+
meta?: MutationMeta
1718
}
1819

1920
export interface MutationState<
@@ -84,6 +85,7 @@ export class Mutation<
8485
state: MutationState<TData, TError, TVariables, TContext>
8586
options: MutationOptions<TData, TError, TVariables, TContext>
8687
mutationId: number
88+
meta: MutationMeta | undefined
8789

8890
private observers: MutationObserver<TData, TError, TVariables, TContext>[]
8991
private mutationCache: MutationCache
@@ -98,6 +100,7 @@ export class Mutation<
98100
this.mutationCache = config.mutationCache
99101
this.observers = []
100102
this.state = config.state || getDefaultState()
103+
this.meta = config.meta
101104
}
102105

103106
setState(state: MutationState<TData, TError, TVariables, TContext>): void {

src/core/mutationCache.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export class MutationCache extends Subscribable<MutationCacheListener> {
5252
defaultOptions: options.mutationKey
5353
? client.getMutationDefaults(options.mutationKey)
5454
: undefined,
55+
meta: options.meta,
5556
})
5657

5758
this.add(mutation)

src/core/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,8 @@ export type MutationKey = string | readonly unknown[]
501501

502502
export type MutationStatus = 'idle' | 'loading' | 'success' | 'error'
503503

504+
export type MutationMeta = Record<string, unknown>
505+
504506
export type MutationFunction<TData = unknown, TVariables = unknown> = (
505507
variables: TVariables
506508
) => Promise<TData>
@@ -536,6 +538,7 @@ export interface MutationOptions<
536538
retry?: RetryValue<TError>
537539
retryDelay?: RetryDelayValue<TError>
538540
_defaulted?: boolean
541+
meta?: MutationMeta
539542
}
540543

541544
export interface MutationObserverOptions<

src/react/tests/useMutation.test.tsx

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,4 +519,68 @@ describe('useMutation', () => {
519519

520520
consoleMock.mockRestore()
521521
})
522+
523+
it('should pass meta to mutation', async () => {
524+
const consoleMock = mockConsoleError()
525+
526+
const errorMock = jest.fn()
527+
const successMock = jest.fn()
528+
529+
const queryClientMutationMeta = new QueryClient({
530+
mutationCache: new MutationCache({
531+
onSuccess: (_, __, ___, mutation) => {
532+
successMock(mutation.meta?.metaSuccessMessage)
533+
},
534+
onError: (_, __, ___, mutation) => {
535+
errorMock(mutation.meta?.metaErrorMessage)
536+
},
537+
}),
538+
})
539+
540+
const metaSuccessMessage = 'mutation succeeded'
541+
const metaErrorMessage = 'mutation failed'
542+
543+
function Page() {
544+
const { mutate: succeed, isSuccess } = useMutation(async () => '', {
545+
meta: { metaSuccessMessage },
546+
})
547+
const { mutate: error, isError } = useMutation(
548+
async () => {
549+
throw new Error('')
550+
},
551+
{
552+
meta: { metaErrorMessage },
553+
}
554+
)
555+
556+
return (
557+
<div>
558+
<button onClick={() => succeed()}>succeed</button>
559+
<button onClick={() => error()}>error</button>
560+
{isSuccess && <div>successTest</div>}
561+
{isError && <div>errorTest</div>}
562+
</div>
563+
)
564+
}
565+
566+
const { getByText, queryByText } = renderWithClient(
567+
queryClientMutationMeta,
568+
<Page />
569+
)
570+
571+
fireEvent.click(getByText('succeed'))
572+
fireEvent.click(getByText('error'))
573+
574+
await waitFor(() => {
575+
expect(queryByText('successTest')).not.toBeNull()
576+
expect(queryByText('errorTest')).not.toBeNull()
577+
})
578+
579+
expect(successMock).toHaveBeenCalledTimes(1)
580+
expect(successMock).toHaveBeenCalledWith(metaSuccessMessage)
581+
expect(errorMock).toHaveBeenCalledTimes(1)
582+
expect(errorMock).toHaveBeenCalledWith(metaErrorMessage)
583+
584+
consoleMock.mockRestore()
585+
})
522586
})

src/react/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
QueryKey,
1010
MutationFunction,
1111
MutateOptions,
12+
MutationMeta,
1213
} from '../core/types'
1314

1415
export interface UseBaseQueryOptions<
@@ -97,6 +98,7 @@ export interface UseMutationOptions<
9798
retry?: RetryValue<TError>
9899
retryDelay?: RetryDelayValue<TError>
99100
useErrorBoundary?: boolean | ((error: TError) => boolean)
101+
meta?: MutationMeta
100102
}
101103

102104
export type UseMutateFunction<

0 commit comments

Comments
 (0)