Repository: tannerlinsley/react-query
Pull request #2906 (2021-11-09 13:24:49)

feat: meta to Mutation
the same meta that already exists in Query and is accessible in
queryCache is now also usable in mutationCache and can be passed in
useMutation, just as in useQuery

Context: https://github.com/TkDodo/blog-comments/issues/29#issuecomment-963095663

vercel[bot]: This pull request is being automatically deployed with Vercel ([learn more](https://vercel.link/github-learn-more)).  
To see the status of your deployment, click below or on the icon next to each commit.

🔍 Inspect: [https://vercel.com/tanstack/react-query/3ZNxqWqUWDNHJa8TdC3vDs9vcx6P](https://vercel.com/tanstack/react-query/3ZNxqWqUWDNHJa8TdC3vDs9vcx6P)  
✅ Preview: *In Progress*


codesandbox[bot]: This pull request is automatically built and testable in [CodeSandbox](https://codesandbox.io).

  To see build info of the built libraries, click [here](https://ci.codesandbox.io/status/tannerlinsley/react-query/pr/2906/builds/186653) or the icon next to each commit SHA.

Latest deployment of this branch, based on commit accf669080eb697c76fe39e7195b5f179f2f2ec0:

|Sandbox| Source |
|--|--|
|[tannerlinsley/react-query: basic](https://codesandbox.io/s/tannerlinsley-react-query-basic-fofde)| Configuration |
|[tannerlinsley/react-query: basic-typescript](https://codesandbox.io/s/tannerlinsley-react-query-basic-typescript-vm757)| Configuration |

codecov[bot]: # [Codecov](https://codecov.io/gh/tannerlinsley/react-query/pull/2906?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley) Report
> Merging [#2906](https://codecov.io/gh/tannerlinsley/react-query/pull/2906?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley) (accf669) into [master](https://codecov.io/gh/tannerlinsley/react-query/commit/c118513e833e92025af4205c29b7e043cd286531?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley) (c118513) will **increase** coverage by `0.00%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/tannerlinsley/react-query/pull/2906/graphs/tree.svg?width=650&height=150&src=pr&token=jqEbswkEDQ&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley)](https://codecov.io/gh/tannerlinsley/react-query/pull/2906?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley)

```diff
@@           Coverage Diff           @@
##           master    #2906   +/-   ##
=======================================
  Coverage   96.40%   96.40%           
=======================================
  Files          45       45           
  Lines        2226     2227    +1     
  Branches      636      636           
=======================================
+ Hits         2146     2147    +1     
  Misses         77       77           
  Partials        3        3           
```


| [Impacted Files](https://codecov.io/gh/tannerlinsley/react-query/pull/2906?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley) | Coverage Δ | |
|---|---|---|
| [src/core/mutationCache.ts](https://codecov.io/gh/tannerlinsley/react-query/pull/2906/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley#diff-c3JjL2NvcmUvbXV0YXRpb25DYWNoZS50cw==) | `100.00% <ø> (ø)` | |
| [src/core/mutation.ts](https://codecov.io/gh/tannerlinsley/react-query/pull/2906/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley#diff-c3JjL2NvcmUvbXV0YXRpb24udHM=) | `100.00% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/tannerlinsley/react-query/pull/2906?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/tannerlinsley/react-query/pull/2906?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley). Last update [c118513...accf669](https://codecov.io/gh/tannerlinsley/react-query/pull/2906?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley).

danielbartsch: b464b49
I hope this is fine

Patch:
diff --git a/docs/src/components/LayoutDocs.js b/docs/src/components/LayoutDocs.js
index cc229a70c..d890d5d94 100644
--- a/docs/src/components/LayoutDocs.js
+++ b/docs/src/components/LayoutDocs.js
@@ -106,7 +106,7 @@ export const LayoutDocs = props => {
                       width: 200,
                     }}
                   >
-                    <div className="sticky top-24 overflow-y-auto">
+                    <div className="sticky top-24">
                       <h4 className="font-semibold uppercase text-sm mb-2 mt-2 text-gray-500">
                         On this page
                       </h4>
diff --git a/docs/src/pages/reference/useMutation.md b/docs/src/pages/reference/useMutation.md
index 1750c9c14..e8b2e9cdc 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<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**
 
diff --git a/src/core/mutation.ts b/src/core/mutation.ts
index 54ba5473a..3846e60ba 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<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<
@@ -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
@@ -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 {
diff --git a/src/core/mutationCache.ts b/src/core/mutationCache.ts
index 02b97b19b..eef98e3ec 100644
--- a/src/core/mutationCache.ts
+++ b/src/core/mutationCache.ts
@@ -52,6 +52,7 @@ export class MutationCache extends Subscribable<MutationCacheListener> {
       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 2aa3dda46..583b34a15 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<string, unknown>
+
 export type MutationFunction<TData = unknown, TVariables = unknown> = (
   variables: TVariables
 ) => Promise<TData>
@@ -536,6 +538,7 @@ export interface MutationOptions<
   retry?: RetryValue<TError>
   retryDelay?: RetryDelayValue<TError>
   _defaulted?: boolean
+  meta?: MutationMeta
 }
 
 export interface MutationObserverOptions<
diff --git a/src/react/types.ts b/src/react/types.ts
index fce406915..79aa3d450 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<TError>
   retryDelay?: RetryDelayValue<TError>
   useErrorBoundary?: boolean | ((error: TError) => boolean)
+  meta?: MutationMeta
 }
 
 export type UseMutateFunction<


Test patch:
diff --git a/src/react/tests/useMutation.test.tsx b/src/react/tests/useMutation.test.tsx
index b253fd235..f4c25e099 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 (
+        <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()
+  })
 })