From 52854795b62946b4a4b73e4bddc6954db77e2058 Mon Sep 17 00:00:00 2001 From: Daniel Bartsch Date: Wed, 17 Nov 2021 12:30:39 +0100 Subject: [PATCH] feat(useMutation): meta for mutations (#2906) --- docs/src/pages/reference/useMutation.md | 4 ++ src/core/mutation.ts | 5 +- src/core/mutationCache.ts | 1 + src/core/types.ts | 3 ++ src/react/tests/useMutation.test.tsx | 64 +++++++++++++++++++++++++ src/react/types.ts | 2 + 6 files changed, 78 insertions(+), 1 deletion(-) diff --git a/docs/src/pages/reference/useMutation.md b/docs/src/pages/reference/useMutation.md index 1750c9c147..e8b2e9cdcb 100644 --- a/docs/src/pages/reference/useMutation.md +++ b/docs/src/pages/reference/useMutation.md @@ -23,6 +23,7 @@ const { onSettled, onSuccess, useErrorBoundary, + meta, }) mutate(variables, { @@ -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` + - 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** diff --git a/src/core/mutation.ts b/src/core/mutation.ts index 54ba5473a9..3846e60ba4 100644 --- a/src/core/mutation.ts +++ b/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' @@ -14,6 +14,7 @@ interface MutationConfig { options: MutationOptions defaultOptions?: MutationOptions state?: MutationState + meta?: MutationMeta } export interface MutationState< @@ -84,6 +85,7 @@ export class Mutation< state: MutationState options: MutationOptions mutationId: number + meta: MutationMeta | undefined private observers: MutationObserver[] private mutationCache: MutationCache @@ -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): void { diff --git a/src/core/mutationCache.ts b/src/core/mutationCache.ts index 02b97b19b4..eef98e3ec4 100644 --- a/src/core/mutationCache.ts +++ b/src/core/mutationCache.ts @@ -52,6 +52,7 @@ export class MutationCache extends Subscribable { defaultOptions: options.mutationKey ? client.getMutationDefaults(options.mutationKey) : undefined, + meta: options.meta, }) this.add(mutation) diff --git a/src/core/types.ts b/src/core/types.ts index 2aa3dda467..583b34a157 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -501,6 +501,8 @@ export type MutationKey = string | readonly unknown[] export type MutationStatus = 'idle' | 'loading' | 'success' | 'error' +export type MutationMeta = Record + export type MutationFunction = ( variables: TVariables ) => Promise @@ -536,6 +538,7 @@ export interface MutationOptions< retry?: RetryValue retryDelay?: RetryDelayValue _defaulted?: boolean + meta?: MutationMeta } export interface MutationObserverOptions< diff --git a/src/react/tests/useMutation.test.tsx b/src/react/tests/useMutation.test.tsx index b253fd2351..f4c25e0998 100644 --- a/src/react/tests/useMutation.test.tsx +++ b/src/react/tests/useMutation.test.tsx @@ -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 ( +
+ + + {isSuccess &&
successTest
} + {isError &&
errorTest
} +
+ ) + } + + const { getByText, queryByText } = renderWithClient( + queryClientMutationMeta, + + ) + + 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() + }) }) diff --git a/src/react/types.ts b/src/react/types.ts index fce4069159..79aa3d4507 100644 --- a/src/react/types.ts +++ b/src/react/types.ts @@ -9,6 +9,7 @@ import { QueryKey, MutationFunction, MutateOptions, + MutationMeta, } from '../core/types' export interface UseBaseQueryOptions< @@ -97,6 +98,7 @@ export interface UseMutationOptions< retry?: RetryValue retryDelay?: RetryDelayValue useErrorBoundary?: boolean | ((error: TError) => boolean) + meta?: MutationMeta } export type UseMutateFunction<