Skip to content

Commit

Permalink
feat(useMutation): meta for mutations (#2906)
Browse files Browse the repository at this point in the history
  • Loading branch information
danielbartsch committed Nov 17, 2021
1 parent 62e12e5 commit 5285479
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 1 deletion.
4 changes: 4 additions & 0 deletions docs/src/pages/reference/useMutation.md
Expand Up @@ -23,6 +23,7 @@ const {
onSettled,
onSuccess,
useErrorBoundary,
meta,
})

mutate(variables, {
Expand Down Expand Up @@ -71,6 +72,9 @@ mutate(variables, {
- Set this to `true` if you want mutation errors to be thrown in the render phase and propagate to the nearest error boundary
- Set this to `false` to disable the behavior of throwing errors to the error boundary.
- 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`)
- `meta: Record<string, unknown>`
- Optional
- 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`).

**Returns**

Expand Down
5 changes: 4 additions & 1 deletion src/core/mutation.ts
@@ -1,4 +1,4 @@
import type { MutationOptions, MutationStatus } from './types'
import type { MutationOptions, MutationStatus, MutationMeta } from './types'
import type { MutationCache } from './mutationCache'
import type { MutationObserver } from './mutationObserver'
import { getLogger } from './logger'
Expand All @@ -14,6 +14,7 @@ interface MutationConfig<TData, TError, TVariables, TContext> {
options: MutationOptions<TData, TError, TVariables, TContext>
defaultOptions?: MutationOptions<TData, TError, TVariables, TContext>
state?: MutationState<TData, TError, TVariables, TContext>
meta?: MutationMeta
}

export interface MutationState<
Expand Down Expand Up @@ -84,6 +85,7 @@ export class Mutation<
state: MutationState<TData, TError, TVariables, TContext>
options: MutationOptions<TData, TError, TVariables, TContext>
mutationId: number
meta: MutationMeta | undefined

private observers: MutationObserver<TData, TError, TVariables, TContext>[]
private mutationCache: MutationCache
Expand All @@ -98,6 +100,7 @@ export class Mutation<
this.mutationCache = config.mutationCache
this.observers = []
this.state = config.state || getDefaultState()
this.meta = config.meta
}

setState(state: MutationState<TData, TError, TVariables, TContext>): void {
Expand Down
1 change: 1 addition & 0 deletions src/core/mutationCache.ts
Expand Up @@ -52,6 +52,7 @@ export class MutationCache extends Subscribable<MutationCacheListener> {
defaultOptions: options.mutationKey
? client.getMutationDefaults(options.mutationKey)
: undefined,
meta: options.meta,
})

this.add(mutation)
Expand Down
3 changes: 3 additions & 0 deletions src/core/types.ts
Expand Up @@ -501,6 +501,8 @@ export type MutationKey = string | readonly unknown[]

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

export type MutationMeta = Record<string, unknown>

export type MutationFunction<TData = unknown, TVariables = unknown> = (
variables: TVariables
) => Promise<TData>
Expand Down Expand Up @@ -536,6 +538,7 @@ export interface MutationOptions<
retry?: RetryValue<TError>
retryDelay?: RetryDelayValue<TError>
_defaulted?: boolean
meta?: MutationMeta
}

export interface MutationObserverOptions<
Expand Down
64 changes: 64 additions & 0 deletions src/react/tests/useMutation.test.tsx
Expand Up @@ -519,4 +519,68 @@ describe('useMutation', () => {

consoleMock.mockRestore()
})

it('should pass meta to mutation', async () => {
const consoleMock = mockConsoleError()

const errorMock = jest.fn()
const successMock = jest.fn()

const queryClientMutationMeta = new QueryClient({
mutationCache: new MutationCache({
onSuccess: (_, __, ___, mutation) => {
successMock(mutation.meta?.metaSuccessMessage)
},
onError: (_, __, ___, mutation) => {
errorMock(mutation.meta?.metaErrorMessage)
},
}),
})

const metaSuccessMessage = 'mutation succeeded'
const metaErrorMessage = 'mutation failed'

function Page() {
const { mutate: succeed, isSuccess } = useMutation(async () => '', {
meta: { metaSuccessMessage },
})
const { mutate: error, isError } = useMutation(
async () => {
throw new Error('')
},
{
meta: { metaErrorMessage },
}
)

return (
<div>
<button onClick={() => succeed()}>succeed</button>
<button onClick={() => error()}>error</button>
{isSuccess && <div>successTest</div>}
{isError && <div>errorTest</div>}
</div>
)
}

const { getByText, queryByText } = renderWithClient(
queryClientMutationMeta,
<Page />
)

fireEvent.click(getByText('succeed'))
fireEvent.click(getByText('error'))

await waitFor(() => {
expect(queryByText('successTest')).not.toBeNull()
expect(queryByText('errorTest')).not.toBeNull()
})

expect(successMock).toHaveBeenCalledTimes(1)
expect(successMock).toHaveBeenCalledWith(metaSuccessMessage)
expect(errorMock).toHaveBeenCalledTimes(1)
expect(errorMock).toHaveBeenCalledWith(metaErrorMessage)

consoleMock.mockRestore()
})
})
2 changes: 2 additions & 0 deletions src/react/types.ts
Expand Up @@ -9,6 +9,7 @@ import {
QueryKey,
MutationFunction,
MutateOptions,
MutationMeta,
} from '../core/types'

export interface UseBaseQueryOptions<
Expand Down Expand Up @@ -97,6 +98,7 @@ export interface UseMutationOptions<
retry?: RetryValue<TError>
retryDelay?: RetryDelayValue<TError>
useErrorBoundary?: boolean | ((error: TError) => boolean)
meta?: MutationMeta
}

export type UseMutateFunction<
Expand Down

1 comment on commit 5285479

@vercel
Copy link

@vercel vercel bot commented on 5285479 Nov 17, 2021

Choose a reason for hiding this comment

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

Please sign in to comment.