diff --git a/docs/framework/angular/guides/mutations.md b/docs/framework/angular/guides/mutations.md index 93405979ba..f511808231 100644 --- a/docs/framework/angular/guides/mutations.md +++ b/docs/framework/angular/guides/mutations.md @@ -89,20 +89,20 @@ export class TodosComponent { ```ts mutation = injectMutation(() => ({ mutationFn: addTodo, - onMutate: (variables) => { + onMutate: (variables, context) => { // A mutation is about to happen! - // Optionally return a context containing data to use when for example rolling back + // Optionally return a result containing data to use when for example rolling back return { id: 1 } }, - onError: (error, variables, context) => { + onError: (error, variables, onMutateResult, context) => { // An error happened! - console.log(`rolling back optimistic update with id ${context.id}`) + console.log(`rolling back optimistic update with id ${onMutateResult.id}`) }, - onSuccess: (data, variables, context) => { + onSuccess: (data, variables, onMutateResult, context) => { // Boom baby! }, - onSettled: (data, error, variables, context) => { + onSettled: (data, error, variables, onMutateResult, context) => { // Error or success... doesn't matter! }, })) @@ -129,25 +129,25 @@ mutation = injectMutation(() => ({ ```ts mutation = injectMutation(() => ({ mutationFn: addTodo, - onSuccess: (data, variables, context) => { + onSuccess: (data, variables, onMutateResult, context) => { // I will fire first }, - onError: (error, variables, context) => { + onError: (error, variables, onMutateResult, context) => { // I will fire first }, - onSettled: (data, error, variables, context) => { + onSettled: (data, error, variables, onMutateResult, context) => { // I will fire first }, })) mutation.mutate(todo, { - onSuccess: (data, variables, context) => { + onSuccess: (data, variables, onMutateResult, context) => { // I will fire second! }, - onError: (error, variables, context) => { + onError: (error, variables, onMutateResult, context) => { // I will fire second! }, - onSettled: (data, error, variables, context) => { + onSettled: (data, error, variables, onMutateResult, context) => { // I will fire second! }, }) @@ -160,7 +160,7 @@ mutation.mutate(todo, { export class Example { mutation = injectMutation(() => ({ mutationFn: addTodo, - onSuccess: (data, variables, context) => { + onSuccess: (data, variables, onMutateResult, context) => { // Will be called 3 times }, })) @@ -168,7 +168,7 @@ export class Example { doMutations() { ;['Todo 1', 'Todo 2', 'Todo 3'].forEach((todo) => { this.mutation.mutate(todo, { - onSuccess: (data, variables, context) => { + onSuccess: (data, variables, onMutateResult, context) => { // Will execute only once, for the last mutation (Todo 3), // regardless which mutation resolves first }, @@ -213,31 +213,31 @@ const queryClient = new QueryClient() // Define the "addTodo" mutation queryClient.setMutationDefaults(['addTodo'], { mutationFn: addTodo, - onMutate: async (variables) => { + onMutate: async (variables, context) => { // Cancel current queries for the todos list - await queryClient.cancelQueries({ queryKey: ['todos'] }) + await context.client.cancelQueries({ queryKey: ['todos'] }) // Create optimistic todo const optimisticTodo = { id: uuid(), title: variables.title } // Add optimistic todo to todos list - queryClient.setQueryData(['todos'], (old) => [...old, optimisticTodo]) + context.client.setQueryData(['todos'], (old) => [...old, optimisticTodo]) - // Return context with the optimistic todo + // Return result with the optimistic todo return { optimisticTodo } }, - onSuccess: (result, variables, context) => { + onSuccess: (result, variables, onMutateResult, context) => { // Replace optimistic todo in the todos list with the result - queryClient.setQueryData(['todos'], (old) => + context.client.setQueryData(['todos'], (old) => old.map((todo) => - todo.id === context.optimisticTodo.id ? result : todo, + todo.id === onMutateResult.optimisticTodo.id ? result : todo, ), ) }, - onError: (error, variables, context) => { + onError: (error, variables, onMutateResult, context) => { // Remove optimistic todo from the todos list - queryClient.setQueryData(['todos'], (old) => - old.filter((todo) => todo.id !== context.optimisticTodo.id), + context.client.setQueryData(['todos'], (old) => + old.filter((todo) => todo.id !== onMutateResult.optimisticTodo.id), ) }, retry: 3, diff --git a/docs/framework/angular/guides/optimistic-updates.md b/docs/framework/angular/guides/optimistic-updates.md index a28bdf8fff..d87a6c28b7 100644 --- a/docs/framework/angular/guides/optimistic-updates.md +++ b/docs/framework/angular/guides/optimistic-updates.md @@ -87,28 +87,28 @@ queryClient = inject(QueryClient) updateTodo = injectMutation(() => ({ mutationFn: updateTodo, // When mutate is called: - onMutate: async (newTodo) => { + onMutate: async (newTodo, context) => { // Cancel any outgoing refetches // (so they don't overwrite our optimistic update) - await this.queryClient.cancelQueries({ queryKey: ['todos'] }) + await context.client.cancelQueries({ queryKey: ['todos'] }) // Snapshot the previous value - const previousTodos = client.getQueryData(['todos']) + const previousTodos = context.client.getQueryData(['todos']) // Optimistically update to the new value - this.queryClient.setQueryData(['todos'], (old) => [...old, newTodo]) + context.client.setQueryData(['todos'], (old) => [...old, newTodo]) - // Return a context object with the snapshotted value + // Return a result object with the snapshotted value return { previousTodos } }, // If the mutation fails, - // use the context returned from onMutate to roll back - onError: (err, newTodo, context) => { - client.setQueryData(['todos'], context.previousTodos) + // use the result returned from onMutate to roll back + onError: (err, newTodo, onMutateResult, context) => { + context.client.setQueryData(['todos'], onMutateResult.previousTodos) }, // Always refetch after error or success: - onSettled: () => { - this.queryClient.invalidateQueries({ queryKey: ['todos'] }) + onSettled: (data, error, variables, onMutateResult, context) => { + context.client.invalidateQueries({ queryKey: ['todos'] }) }, })) ``` @@ -122,30 +122,30 @@ queryClient = inject(QueryClient) updateTodo = injectMutation(() => ({ mutationFn: updateTodo, // When mutate is called: - onMutate: async (newTodo) => { + onMutate: async (newTodo, context) => { // Cancel any outgoing refetches // (so they don't overwrite our optimistic update) - await this.queryClient.cancelQueries({ queryKey: ['todos', newTodo.id] }) + await context.client.cancelQueries({ queryKey: ['todos', newTodo.id] }) // Snapshot the previous value - const previousTodo = this.queryClient.getQueryData(['todos', newTodo.id]) + const previousTodo = context.client.getQueryData(['todos', newTodo.id]) // Optimistically update to the new value - this.queryClient.setQueryData(['todos', newTodo.id], newTodo) + context.client.setQueryData(['todos', newTodo.id], newTodo) - // Return a context with the previous and new todo + // Return a result with the previous and new todo return { previousTodo, newTodo } }, - // If the mutation fails, use the context we returned above - onError: (err, newTodo, context) => { - this.queryClient.setQueryData( - ['todos', context.newTodo.id], - context.previousTodo, + // If the mutation fails, use the result we returned above + onError: (err, newTodo, onMutateResult, context) => { + context.client.setQueryData( + ['todos', onMutateResult.newTodo.id], + onMutateResult.previousTodo, ) }, // Always refetch after error or success: - onSettled: (newTodo) => { - this.queryClient.invalidateQueries({ queryKey: ['todos', newTodo.id] }) + onSettled: (newTodo, error, variables, onMutateResult, context) => { + context.client.invalidateQueries({ queryKey: ['todos', newTodo.id] }) }, })) ``` @@ -157,7 +157,7 @@ updateTodo = injectMutation(() => ({ injectMutation({ mutationFn: updateTodo, // ... - onSettled: (newTodo, error, variables, context) => { + onSettled: (newTodo, error, variables, onMutateResult, context) => { if (error) { // do something } diff --git a/docs/framework/angular/reference/functions/injectmutation.md b/docs/framework/angular/reference/functions/injectmutation.md index bb6abf188f..8a18f6d36c 100644 --- a/docs/framework/angular/reference/functions/injectmutation.md +++ b/docs/framework/angular/reference/functions/injectmutation.md @@ -8,10 +8,10 @@ title: injectMutation # Function: injectMutation() ```ts -function injectMutation( +function injectMutation( injectMutationFn, options?, -): CreateMutationResult +): CreateMutationResult ``` Defined in: [inject-mutation.ts:44](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-mutation.ts#L44) @@ -28,13 +28,13 @@ Unlike queries, mutations are not run automatically. • **TVariables** = `void` -• **TContext** = `unknown` +• **TOnMutateResult** = `unknown` ## Parameters ### injectMutationFn -() => [`CreateMutationOptions`](../../interfaces/createmutationoptions.md)\<`TData`, `TError`, `TVariables`, `TContext`\> +() => [`CreateMutationOptions`](../../interfaces/createmutationoptions.md)\<`TData`, `TError`, `TVariables`, `TOnMutateResult`\> A function that returns mutation options. @@ -46,6 +46,6 @@ Additional configuration ## Returns -[`CreateMutationResult`](../../type-aliases/createmutationresult.md)\<`TData`, `TError`, `TVariables`, `TContext`\> +[`CreateMutationResult`](../../type-aliases/createmutationresult.md)\<`TData`, `TError`, `TVariables`, `TOnMutateResult`\> The mutation. diff --git a/docs/framework/angular/reference/functions/mutationoptions.md b/docs/framework/angular/reference/functions/mutationoptions.md index 46e3844f40..0284da3771 100644 --- a/docs/framework/angular/reference/functions/mutationoptions.md +++ b/docs/framework/angular/reference/functions/mutationoptions.md @@ -45,10 +45,10 @@ The mutation options. ## Call Signature ```ts -function mutationOptions( +function mutationOptions( options, ): WithRequired< - CreateMutationOptions, + CreateMutationOptions, 'mutationKey' > ``` @@ -94,19 +94,19 @@ class ComponentOrService { • **TVariables** = `void` -• **TContext** = `unknown` +• **TOnMutateResult** = `unknown` ### Parameters #### options -`WithRequired`\<[`CreateMutationOptions`](../../interfaces/createmutationoptions.md)\<`TData`, `TError`, `TVariables`, `TContext`\>, `"mutationKey"`\> +`WithRequired`\<[`CreateMutationOptions`](../../interfaces/createmutationoptions.md)\<`TData`, `TError`, `TVariables`, `TOnMutateResult`\>, `"mutationKey"`\> The mutation options. ### Returns -`WithRequired`\<[`CreateMutationOptions`](../../interfaces/createmutationoptions.md)\<`TData`, `TError`, `TVariables`, `TContext`\>, `"mutationKey"`\> +`WithRequired`\<[`CreateMutationOptions`](../../interfaces/createmutationoptions.md)\<`TData`, `TError`, `TVariables`, `TOnMutateResult`\>, `"mutationKey"`\> Mutation options. @@ -119,10 +119,10 @@ The mutation options. ## Call Signature ```ts -function mutationOptions( +function mutationOptions( options, ): Omit< - CreateMutationOptions, + CreateMutationOptions, 'mutationKey' > ``` @@ -168,19 +168,19 @@ class ComponentOrService { • **TVariables** = `void` -• **TContext** = `unknown` +• **TOnMutateResult** = `unknown` ### Parameters #### options -`Omit`\<[`CreateMutationOptions`](../../interfaces/createmutationoptions.md)\<`TData`, `TError`, `TVariables`, `TContext`\>, `"mutationKey"`\> +`Omit`\<[`CreateMutationOptions`](../../interfaces/createmutationoptions.md)\<`TData`, `TError`, `TVariables`, `TOnMutateResult`\>, `"mutationKey"`\> The mutation options. ### Returns -`Omit`\<[`CreateMutationOptions`](../../interfaces/createmutationoptions.md)\<`TData`, `TError`, `TVariables`, `TContext`\>, `"mutationKey"`\> +`Omit`\<[`CreateMutationOptions`](../../interfaces/createmutationoptions.md)\<`TData`, `TError`, `TVariables`, `TOnMutateResult`\>, `"mutationKey"`\> Mutation options. diff --git a/docs/framework/angular/reference/interfaces/basemutationnarrowing.md b/docs/framework/angular/reference/interfaces/basemutationnarrowing.md index 8ea0f00f90..157ed2d305 100644 --- a/docs/framework/angular/reference/interfaces/basemutationnarrowing.md +++ b/docs/framework/angular/reference/interfaces/basemutationnarrowing.md @@ -5,7 +5,7 @@ title: BaseMutationNarrowing -# Interface: BaseMutationNarrowing\ +# Interface: BaseMutationNarrowing\ Defined in: [types.ts:183](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L183) @@ -17,7 +17,7 @@ Defined in: [types.ts:183](https://github.com/TanStack/query/blob/main/packages/ • **TVariables** = `unknown` -• **TContext** = `unknown` +• **TOnMutateResult** = `unknown` ## Properties @@ -29,16 +29,18 @@ isError: SignalFunction< TData, TError, TVariables, - TContext, + TOnMutateResult, Override< - MutationObserverErrorResult, - { mutate: CreateMutateFunction } + MutationObserverErrorResult, + { + mutate: CreateMutateFunction + } > & { mutateAsync: CreateMutateAsyncFunction< TData, TError, TVariables, - TContext + TOnMutateResult > } > @@ -57,16 +59,18 @@ isIdle: SignalFunction< TData, TError, TVariables, - TContext, + TOnMutateResult, Override< - MutationObserverIdleResult, - { mutate: CreateMutateFunction } + MutationObserverIdleResult, + { + mutate: CreateMutateFunction + } > & { mutateAsync: CreateMutateAsyncFunction< TData, TError, TVariables, - TContext + TOnMutateResult > } > @@ -85,16 +89,18 @@ isPending: SignalFunction< TData, TError, TVariables, - TContext, + TOnMutateResult, Override< - MutationObserverLoadingResult, - { mutate: CreateMutateFunction } + MutationObserverLoadingResult, + { + mutate: CreateMutateFunction + } > & { mutateAsync: CreateMutateAsyncFunction< TData, TError, TVariables, - TContext + TOnMutateResult > } > @@ -113,16 +119,18 @@ isSuccess: SignalFunction< TData, TError, TVariables, - TContext, + TOnMutateResult, Override< - MutationObserverSuccessResult, - { mutate: CreateMutateFunction } + MutationObserverSuccessResult, + { + mutate: CreateMutateFunction + } > & { mutateAsync: CreateMutateAsyncFunction< TData, TError, TVariables, - TContext + TOnMutateResult > } > diff --git a/docs/framework/angular/reference/interfaces/createmutationoptions.md b/docs/framework/angular/reference/interfaces/createmutationoptions.md index ebc47be190..372be3a305 100644 --- a/docs/framework/angular/reference/interfaces/createmutationoptions.md +++ b/docs/framework/angular/reference/interfaces/createmutationoptions.md @@ -5,13 +5,13 @@ title: CreateMutationOptions -# Interface: CreateMutationOptions\ +# Interface: CreateMutationOptions\ Defined in: [types.ts:132](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L132) ## Extends -- `OmitKeyof`\<`MutationObserverOptions`\<`TData`, `TError`, `TVariables`, `TContext`\>, `"_defaulted"`\> +- `OmitKeyof`\<`MutationObserverOptions`\<`TData`, `TError`, `TVariables`, `TOnMutateResult`\>, `"_defaulted"`\> ## Type Parameters @@ -21,4 +21,4 @@ Defined in: [types.ts:132](https://github.com/TanStack/query/blob/main/packages/ • **TVariables** = `void` -• **TContext** = `unknown` +• **TOnMutateResult** = `unknown` diff --git a/docs/framework/angular/reference/type-aliases/createbasemutationresult.md b/docs/framework/angular/reference/type-aliases/createbasemutationresult.md index 39441b5f12..4c33f30311 100644 --- a/docs/framework/angular/reference/type-aliases/createbasemutationresult.md +++ b/docs/framework/angular/reference/type-aliases/createbasemutationresult.md @@ -5,16 +5,17 @@ title: CreateBaseMutationResult -# Type Alias: CreateBaseMutationResult\ +# Type Alias: CreateBaseMutationResult\ ```ts -type CreateBaseMutationResult = Override< - MutationObserverResult, - { - mutate: CreateMutateFunction - } -> & - object +type CreateBaseMutationResult = + Override< + MutationObserverResult, + { + mutate: CreateMutateFunction + } + > & + object ``` Defined in: [types.ts:158](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L158) @@ -24,7 +25,12 @@ Defined in: [types.ts:158](https://github.com/TanStack/query/blob/main/packages/ ### mutateAsync ```ts -mutateAsync: CreateMutateAsyncFunction +mutateAsync: CreateMutateAsyncFunction< + TData, + TError, + TVariables, + TOnMutateResult +> ``` ## Type Parameters @@ -35,4 +41,4 @@ mutateAsync: CreateMutateAsyncFunction • **TVariables** = `unknown` -• **TContext** = `unknown` +• **TOnMutateResult** = `unknown` diff --git a/docs/framework/angular/reference/type-aliases/createmutateasyncfunction.md b/docs/framework/angular/reference/type-aliases/createmutateasyncfunction.md index abe1503a1d..d99ecc82d3 100644 --- a/docs/framework/angular/reference/type-aliases/createmutateasyncfunction.md +++ b/docs/framework/angular/reference/type-aliases/createmutateasyncfunction.md @@ -5,11 +5,11 @@ title: CreateMutateAsyncFunction -# Type Alias: CreateMutateAsyncFunction\ +# Type Alias: CreateMutateAsyncFunction\ ```ts -type CreateMutateAsyncFunction = - MutateFunction +type CreateMutateAsyncFunction = + MutateFunction ``` Defined in: [types.ts:151](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L151) @@ -22,4 +22,4 @@ Defined in: [types.ts:151](https://github.com/TanStack/query/blob/main/packages/ • **TVariables** = `void` -• **TContext** = `unknown` +• **TOnMutateResult** = `unknown` diff --git a/docs/framework/angular/reference/type-aliases/createmutatefunction.md b/docs/framework/angular/reference/type-aliases/createmutatefunction.md index dba0f7aa6e..424686ed75 100644 --- a/docs/framework/angular/reference/type-aliases/createmutatefunction.md +++ b/docs/framework/angular/reference/type-aliases/createmutatefunction.md @@ -5,10 +5,10 @@ title: CreateMutateFunction -# Type Alias: CreateMutateFunction()\ +# Type Alias: CreateMutateFunction()\ ```ts -type CreateMutateFunction = ( +type CreateMutateFunction = ( ...args ) => void ``` @@ -23,13 +23,13 @@ Defined in: [types.ts:142](https://github.com/TanStack/query/blob/main/packages/ • **TVariables** = `void` -• **TContext** = `unknown` +• **TOnMutateResult** = `unknown` ## Parameters ### args -...`Parameters`\<`MutateFunction`\<`TData`, `TError`, `TVariables`, `TContext`\>\> +...`Parameters`\<`MutateFunction`\<`TData`, `TError`, `TVariables`, `TOnMutateResult`\>\> ## Returns diff --git a/docs/framework/angular/reference/type-aliases/createmutationresult.md b/docs/framework/angular/reference/type-aliases/createmutationresult.md index 0800010d05..0b2ceb3a70 100644 --- a/docs/framework/angular/reference/type-aliases/createmutationresult.md +++ b/docs/framework/angular/reference/type-aliases/createmutationresult.md @@ -5,11 +5,11 @@ title: CreateMutationResult -# Type Alias: CreateMutationResult\ +# Type Alias: CreateMutationResult\ ```ts -type CreateMutationResult = - BaseMutationNarrowing & +type CreateMutationResult = + BaseMutationNarrowing & MapToSignals> ``` @@ -23,6 +23,6 @@ Defined in: [types.ts:259](https://github.com/TanStack/query/blob/main/packages/ • **TVariables** = `unknown` -• **TContext** = `unknown` +• **TOnMutateResult** = `unknown` -• **TState** = `CreateStatusBasedMutationResult`\<[`CreateBaseMutationResult`](../createbasemutationresult.md)\[`"status"`\], `TData`, `TError`, `TVariables`, `TContext`\> +• **TState** = `CreateStatusBasedMutationResult`\<[`CreateBaseMutationResult`](../createbasemutationresult.md)\[`"status"`\], `TData`, `TError`, `TVariables`, `TOnMutateResult`\> diff --git a/docs/framework/react/guides/mutations.md b/docs/framework/react/guides/mutations.md index 22af057a3e..925954bf09 100644 --- a/docs/framework/react/guides/mutations.md +++ b/docs/framework/react/guides/mutations.md @@ -143,20 +143,20 @@ const CreateTodo = () => { ```tsx useMutation({ mutationFn: addTodo, - onMutate: (variables) => { + onMutate: (variables, context) => { // A mutation is about to happen! - // Optionally return a context containing data to use when for example rolling back + // Optionally return a result containing data to use when for example rolling back return { id: 1 } }, - onError: (error, variables, context) => { + onError: (error, variables, onMutateResult, context) => { // An error happened! - console.log(`rolling back optimistic update with id ${context.id}`) + console.log(`rolling back optimistic update with id ${onMutateResult.id}`) }, - onSuccess: (data, variables, context) => { + onSuccess: (data, variables, onMutateResult, context) => { // Boom baby! }, - onSettled: (data, error, variables, context) => { + onSettled: (data, error, variables, onMutateResult, context) => { // Error or success... doesn't matter! }, }) @@ -189,25 +189,25 @@ You might find that you want to **trigger additional callbacks** beyond the ones ```tsx useMutation({ mutationFn: addTodo, - onSuccess: (data, variables, context) => { + onSuccess: (data, variables, onMutateResult, context) => { // I will fire first }, - onError: (error, variables, context) => { + onError: (error, variables, onMutateResult, context) => { // I will fire first }, - onSettled: (data, error, variables, context) => { + onSettled: (data, error, variables, onMutateResult, context) => { // I will fire first }, }) mutate(todo, { - onSuccess: (data, variables, context) => { + onSuccess: (data, variables, onMutateResult, context) => { // I will fire second! }, - onError: (error, variables, context) => { + onError: (error, variables, onMutateResult, context) => { // I will fire second! }, - onSettled: (data, error, variables, context) => { + onSettled: (data, error, variables, onMutateResult, context) => { // I will fire second! }, }) @@ -226,7 +226,7 @@ There is a slight difference in handling `onSuccess`, `onError` and `onSettled` ```tsx useMutation({ mutationFn: addTodo, - onSuccess: (data, variables, context) => { + onSuccess: (data, variables, onMutateResult, context) => { // Will be called 3 times }, }) @@ -234,7 +234,7 @@ useMutation({ const todos = ['Todo 1', 'Todo 2', 'Todo 3'] todos.forEach((todo) => { mutate(todo, { - onSuccess: (data, variables, context) => { + onSuccess: (data, variables, onMutateResult, context) => { // Will execute only once, for the last mutation (Todo 3), // regardless which mutation resolves first }, @@ -294,31 +294,31 @@ const queryClient = new QueryClient() // Define the "addTodo" mutation queryClient.setMutationDefaults(['addTodo'], { mutationFn: addTodo, - onMutate: async (variables) => { + onMutate: async (variables, context) => { // Cancel current queries for the todos list - await queryClient.cancelQueries({ queryKey: ['todos'] }) + await context.client.cancelQueries({ queryKey: ['todos'] }) // Create optimistic todo const optimisticTodo = { id: uuid(), title: variables.title } // Add optimistic todo to todos list - queryClient.setQueryData(['todos'], (old) => [...old, optimisticTodo]) + context.client.setQueryData(['todos'], (old) => [...old, optimisticTodo]) - // Return context with the optimistic todo + // Return a result with the optimistic todo return { optimisticTodo } }, - onSuccess: (result, variables, context) => { + onSuccess: (result, variables, onMutateResult, context) => { // Replace optimistic todo in the todos list with the result - queryClient.setQueryData(['todos'], (old) => + context.client.setQueryData(['todos'], (old) => old.map((todo) => - todo.id === context.optimisticTodo.id ? result : todo, + todo.id === onMutateResult.optimisticTodo.id ? result : todo, ), ) }, - onError: (error, variables, context) => { + onError: (error, variables, onMutateResult, context) => { // Remove optimistic todo from the todos list - queryClient.setQueryData(['todos'], (old) => - old.filter((todo) => todo.id !== context.optimisticTodo.id), + context.client.setQueryData(['todos'], (old) => + old.filter((todo) => todo.id !== onMutateResult.optimisticTodo.id), ) }, retry: 3, diff --git a/docs/framework/react/guides/optimistic-updates.md b/docs/framework/react/guides/optimistic-updates.md index 1c04627646..27b958dd97 100644 --- a/docs/framework/react/guides/optimistic-updates.md +++ b/docs/framework/react/guides/optimistic-updates.md @@ -99,27 +99,28 @@ const queryClient = useQueryClient() useMutation({ mutationFn: updateTodo, // When mutate is called: - onMutate: async (newTodo) => { + onMutate: async (newTodo, context) => { // Cancel any outgoing refetches // (so they don't overwrite our optimistic update) - await queryClient.cancelQueries({ queryKey: ['todos'] }) + await context.client.cancelQueries({ queryKey: ['todos'] }) // Snapshot the previous value - const previousTodos = queryClient.getQueryData(['todos']) + const previousTodos = context.client.getQueryData(['todos']) // Optimistically update to the new value - queryClient.setQueryData(['todos'], (old) => [...old, newTodo]) + context.client.setQueryData(['todos'], (old) => [...old, newTodo]) - // Return a context object with the snapshotted value + // Return a result with the snapshotted value return { previousTodos } }, // If the mutation fails, - // use the context returned from onMutate to roll back - onError: (err, newTodo, context) => { - queryClient.setQueryData(['todos'], context.previousTodos) + // use the result returned from onMutate to roll back + onError: (err, newTodo, onMutateResult, context) => { + context.client.setQueryData(['todos'], onMutateResult.previousTodos) }, // Always refetch after error or success: - onSettled: () => queryClient.invalidateQueries({ queryKey: ['todos'] }), + onSettled: (data, error, variables, onMutateResult, context) => + context.client.invalidateQueries({ queryKey: ['todos'] }), }) ``` @@ -133,30 +134,30 @@ useMutation({ useMutation({ mutationFn: updateTodo, // When mutate is called: - onMutate: async (newTodo) => { + onMutate: async (newTodo, context) => { // Cancel any outgoing refetches // (so they don't overwrite our optimistic update) - await queryClient.cancelQueries({ queryKey: ['todos', newTodo.id] }) + await context.client.cancelQueries({ queryKey: ['todos', newTodo.id] }) // Snapshot the previous value - const previousTodo = queryClient.getQueryData(['todos', newTodo.id]) + const previousTodo = context.client.getQueryData(['todos', newTodo.id]) // Optimistically update to the new value - queryClient.setQueryData(['todos', newTodo.id], newTodo) + context.client.setQueryData(['todos', newTodo.id], newTodo) - // Return a context with the previous and new todo + // Return a result with the previous and new todo return { previousTodo, newTodo } }, - // If the mutation fails, use the context we returned above - onError: (err, newTodo, context) => { - queryClient.setQueryData( - ['todos', context.newTodo.id], - context.previousTodo, + // If the mutation fails, use the result we returned above + onError: (err, newTodo, onMutateResult, context) => { + context.client.setQueryData( + ['todos', onMutateResult.newTodo.id], + onMutateResult.previousTodo, ) }, // Always refetch after error or success: - onSettled: (newTodo) => - queryClient.invalidateQueries({ queryKey: ['todos', newTodo.id] }), + onSettled: (newTodo, error, variables, onMutateResult, context) => + context.client.invalidateQueries({ queryKey: ['todos', newTodo.id] }), }) ``` @@ -170,7 +171,7 @@ You can also use the `onSettled` function in place of the separate `onError` and useMutation({ mutationFn: updateTodo, // ... - onSettled: async (newTodo, error, variables, context) => { + onSettled: async (newTodo, error, variables, onMutateResult, context) => { if (error) { // do something } diff --git a/docs/framework/react/reference/useMutation.md b/docs/framework/react/reference/useMutation.md index 76d88900df..889bf08423 100644 --- a/docs/framework/react/reference/useMutation.md +++ b/docs/framework/react/reference/useMutation.md @@ -48,10 +48,11 @@ mutate(variables, { **Parameter1 (Options)** -- `mutationFn: (variables: TVariables) => Promise` +- `mutationFn: (variables: TVariables, context: MutationFunctionContext) => Promise` - **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 that `mutate` will pass to your `mutationFn`. Contains reference to `QueryClient`, `mutationKey` and optional `meta` object. - `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 @@ -63,20 +64,20 @@ mutate(variables, { - Optional - defaults to `'online'` - see [Network Mode](../../guides/network-mode.md) for more information. -- `onMutate: (variables: TVariables) => Promise | TContext | void` +- `onMutate: (variables: TVariables) => Promise | TOnMutateResult | void` - Optional - This function will fire before the mutation function is fired and is passed the same variables the mutation function would receive - Useful to perform optimistic updates to a resource in hopes that the mutation succeeds - The value returned from this function will be passed to both the `onError` and `onSettled` functions in the event of a mutation failure and can be useful for rolling back optimistic updates. -- `onSuccess: (data: TData, variables: TVariables, context: TContext) => Promise | unknown` +- `onSuccess: (data: TData, variables: TVariables, onMutateResult: TOnMutateResult | undefined, context: MutationFunctionContext) => Promise | unknown` - Optional - This function will fire when the mutation is successful and will be passed the mutation's result. - If a promise is returned, it will be awaited and resolved before proceeding -- `onError: (err: TError, variables: TVariables, context?: TContext) => Promise | unknown` +- `onError: (err: TError, variables: TVariables, onMutateResult: TOnMutateResult | undefined, context: MutationFunctionContext) => Promise | unknown` - Optional - This function will fire if the mutation encounters an error and will be passed the error. - If a promise is returned, it will be awaited and resolved before proceeding -- `onSettled: (data: TData, error: TError, variables: TVariables, context?: TContext) => Promise | unknown` +- `onSettled: (data: TData, error: TError, variables: TVariables, onMutateResult: TOnMutateResult | undefined, context: MutationFunctionContext) => Promise | unknown` - Optional - This function will fire when the mutation is either successfully fetched or encounters an error and be passed either the data or error - If a promise is returned, it will be awaited and resolved before proceeding @@ -113,15 +114,15 @@ mutate(variables, { - `variables: TVariables` - Optional - The variables object to pass to the `mutationFn`. - - `onSuccess: (data: TData, variables: TVariables, context: TContext) => void` + - `onSuccess: (data: TData, variables: TVariables, onMutateResult: TOnMutateResult | undefined, context: MutationFunctionContext) => void` - Optional - This function will fire when the mutation is successful and will be passed the mutation's result. - Void function, the returned value will be ignored - - `onError: (err: TError, variables: TVariables, context: TContext | undefined) => void` + - `onError: (err: TError, variables: TVariables, onMutateResult: TOnMutateResult | undefined, context: MutationFunctionContext) => void` - Optional - This function will fire if the mutation encounters an error and will be passed the error. - Void function, the returned value will be ignored - - `onSettled: (data: TData | undefined, error: TError | null, variables: TVariables, context: TContext | undefined) => void` + - `onSettled: (data: TData | undefined, error: TError | null, variables: TVariables, onMutateResult: TOnMutateResult | undefined, context: MutationFunctionContext) => void` - Optional - This function will fire when the mutation is either successfully fetched or encounters an error and be passed either the data or error - Void function, the returned value will be ignored diff --git a/docs/framework/svelte/reference/functions/createmutation.md b/docs/framework/svelte/reference/functions/createmutation.md index 8c5e79cff9..bdcac9262c 100644 --- a/docs/framework/svelte/reference/functions/createmutation.md +++ b/docs/framework/svelte/reference/functions/createmutation.md @@ -6,10 +6,10 @@ title: createMutation # Function: createMutation() ```ts -function createMutation( +function createMutation( options, queryClient?, -): CreateMutationResult +): CreateMutationResult ``` ## Type Parameters @@ -20,13 +20,13 @@ function createMutation( • **TVariables** = `void` -• **TContext** = `unknown` +• **TOnMutateResult** = `unknown` ## Parameters ### options -[`StoreOrVal`](../../type-aliases/storeorval.md)\<[`CreateMutationOptions`](../../type-aliases/createmutationoptions.md)\<`TData`, `TError`, `TVariables`, `TContext`\>\> +[`StoreOrVal`](../../type-aliases/storeorval.md)\<[`CreateMutationOptions`](../../type-aliases/createmutationoptions.md)\<`TData`, `TError`, `TVariables`, `TOnMutateResult`\>\> ### queryClient? @@ -34,7 +34,7 @@ function createMutation( ## Returns -[`CreateMutationResult`](../../type-aliases/createmutationresult.md)\<`TData`, `TError`, `TVariables`, `TContext`\> +[`CreateMutationResult`](../../type-aliases/createmutationresult.md)\<`TData`, `TError`, `TVariables`, `TOnMutateResult`\> ## Defined in diff --git a/docs/framework/svelte/reference/type-aliases/createbasemutationresult.md b/docs/framework/svelte/reference/type-aliases/createbasemutationresult.md index 9d51cbc3b2..1ba4b9e226 100644 --- a/docs/framework/svelte/reference/type-aliases/createbasemutationresult.md +++ b/docs/framework/svelte/reference/type-aliases/createbasemutationresult.md @@ -3,16 +3,17 @@ id: CreateBaseMutationResult title: CreateBaseMutationResult --- -# Type Alias: CreateBaseMutationResult\ +# Type Alias: CreateBaseMutationResult\ ```ts -type CreateBaseMutationResult = Override< - MutationObserverResult, - { - mutate: CreateMutateFunction - } -> & - object +type CreateBaseMutationResult = + Override< + MutationObserverResult, + { + mutate: CreateMutateFunction + } + > & + object ``` ## Type declaration @@ -20,7 +21,12 @@ type CreateBaseMutationResult = Override< ### mutateAsync ```ts -mutateAsync: CreateMutateAsyncFunction +mutateAsync: CreateMutateAsyncFunction< + TData, + TError, + TVariables, + TOnMutateResult +> ``` ## Type Parameters @@ -31,7 +37,7 @@ mutateAsync: CreateMutateAsyncFunction • **TVariables** = `unknown` -• **TContext** = `unknown` +• **TOnMutateResult** = `unknown` ## Defined in diff --git a/docs/framework/svelte/reference/type-aliases/createmutateasyncfunction.md b/docs/framework/svelte/reference/type-aliases/createmutateasyncfunction.md index 31162afcd4..75cff15382 100644 --- a/docs/framework/svelte/reference/type-aliases/createmutateasyncfunction.md +++ b/docs/framework/svelte/reference/type-aliases/createmutateasyncfunction.md @@ -3,11 +3,11 @@ id: CreateMutateAsyncFunction title: CreateMutateAsyncFunction --- -# Type Alias: CreateMutateAsyncFunction\ +# Type Alias: CreateMutateAsyncFunction\ ```ts -type CreateMutateAsyncFunction = - MutateFunction +type CreateMutateAsyncFunction = + MutateFunction ``` ## Type Parameters @@ -18,7 +18,7 @@ type CreateMutateAsyncFunction = • **TVariables** = `void` -• **TContext** = `unknown` +• **TOnMutateResult** = `unknown` ## Defined in diff --git a/docs/framework/svelte/reference/type-aliases/createmutatefunction.md b/docs/framework/svelte/reference/type-aliases/createmutatefunction.md index 277affff10..05c68ed144 100644 --- a/docs/framework/svelte/reference/type-aliases/createmutatefunction.md +++ b/docs/framework/svelte/reference/type-aliases/createmutatefunction.md @@ -3,10 +3,10 @@ id: CreateMutateFunction title: CreateMutateFunction --- -# Type Alias: CreateMutateFunction()\ +# Type Alias: CreateMutateFunction()\ ```ts -type CreateMutateFunction = ( +type CreateMutateFunction = ( ...args ) => void ``` @@ -19,13 +19,13 @@ type CreateMutateFunction = ( • **TVariables** = `void` -• **TContext** = `unknown` +• **TOnMutateResult** = `unknown` ## Parameters ### args -...`Parameters`\<`MutateFunction`\<`TData`, `TError`, `TVariables`, `TContext`\>\> +...`Parameters`\<`MutateFunction`\<`TData`, `TError`, `TVariables`, `TOnMutateResult`\>\> ## Returns diff --git a/docs/framework/svelte/reference/type-aliases/createmutationoptions.md b/docs/framework/svelte/reference/type-aliases/createmutationoptions.md index 6f1fd3ab96..a073d5294b 100644 --- a/docs/framework/svelte/reference/type-aliases/createmutationoptions.md +++ b/docs/framework/svelte/reference/type-aliases/createmutationoptions.md @@ -3,13 +3,14 @@ id: CreateMutationOptions title: CreateMutationOptions --- -# Type Alias: CreateMutationOptions\ +# Type Alias: CreateMutationOptions\ ```ts -type CreateMutationOptions = OmitKeyof< - MutationObserverOptions, - '_defaulted' -> +type CreateMutationOptions = + OmitKeyof< + MutationObserverOptions, + '_defaulted' + > ``` Options for createMutation @@ -22,7 +23,7 @@ Options for createMutation • **TVariables** = `void` -• **TContext** = `unknown` +• **TOnMutateResult** = `unknown` ## Defined in diff --git a/docs/framework/svelte/reference/type-aliases/createmutationresult.md b/docs/framework/svelte/reference/type-aliases/createmutationresult.md index 9e6fae8dbb..905204c5fa 100644 --- a/docs/framework/svelte/reference/type-aliases/createmutationresult.md +++ b/docs/framework/svelte/reference/type-aliases/createmutationresult.md @@ -3,12 +3,11 @@ id: CreateMutationResult title: CreateMutationResult --- -# Type Alias: CreateMutationResult\ +# Type Alias: CreateMutationResult\ ```ts -type CreateMutationResult = Readable< - CreateBaseMutationResult -> +type CreateMutationResult = + Readable> ``` Result from createMutation @@ -21,7 +20,7 @@ Result from createMutation • **TVariables** = `unknown` -• **TContext** = `unknown` +• **TOnMutateResult** = `unknown` ## Defined in diff --git a/docs/reference/MutationCache.md b/docs/reference/MutationCache.md index 9e8ef2e61a..4df1e68a1e 100644 --- a/docs/reference/MutationCache.md +++ b/docs/reference/MutationCache.md @@ -28,19 +28,19 @@ Its available methods are: **Options** -- `onError?: (error: unknown, variables: unknown, context: unknown, mutation: Mutation) => Promise | unknown` +- `onError?: (error: unknown, variables: unknown, onMutateResult: unknown, mutation: Mutation, mutationFnContext: MutationFunctionContext) => Promise | unknown` - Optional - This function will be called if some mutation encounters an error. - If you return a Promise from it, it will be awaited -- `onSuccess?: (data: unknown, variables: unknown, context: unknown, mutation: Mutation) => Promise | unknown` +- `onSuccess?: (data: unknown, variables: unknown, onMutateResult: unknown, mutation: Mutation, mutationFnContext: MutationFunctionContext) => Promise | unknown` - Optional - This function will be called if some mutation is successful. - If you return a Promise from it, it will be awaited -- `onSettled?: (data: unknown | undefined, error: unknown | null, variables: unknown, context: unknown, mutation: Mutation) => Promise | unknown` +- `onSettled?: (data: unknown | undefined, error: unknown | null, variables: unknown, onMutateResult: unknown, mutation: Mutation, mutationFnContext: MutationFunctionContext) => Promise | unknown` - Optional - This function will be called if some mutation is settled (either successful or errored). - If you return a Promise from it, it will be awaited -- `onMutate?: (variables: unknown, mutation: Mutation) => Promise | unknown` +- `onMutate?: (variables: unknown, mutation: Mutation, mutationFnContext: MutationFunctionContext) => Promise | unknown` - Optional - This function will be called before some mutation executes. - If you return a Promise from it, it will be awaited @@ -50,7 +50,7 @@ Its available methods are: The `onError`, `onSuccess`, `onSettled` and `onMutate` callbacks on the MutationCache can be used to handle these events on a global level. They are different to `defaultOptions` provided to the QueryClient because: - `defaultOptions` can be overridden by each Mutation - the global callbacks will **always** be called. -- `onMutate` does not allow returning a context value. +- `onMutate` does not allow returning a result. ## `mutationCache.getAll` diff --git a/examples/react/optimistic-updates-cache/src/pages/index.tsx b/examples/react/optimistic-updates-cache/src/pages/index.tsx index e9c24a7411..be8a4fcd89 100644 --- a/examples/react/optimistic-updates-cache/src/pages/index.tsx +++ b/examples/react/optimistic-updates-cache/src/pages/index.tsx @@ -45,18 +45,20 @@ function Example() { return await response.json() }, // When mutate is called: - onMutate: async (newTodo: string) => { + onMutate: async (newTodo, context) => { setText('') // Cancel any outgoing refetch // (so they don't overwrite our optimistic update) - await queryClient.cancelQueries(todoListOptions) + await context.client.cancelQueries(todoListOptions) // Snapshot the previous value - const previousTodos = queryClient.getQueryData(todoListOptions.queryKey) + const previousTodos = context.client.getQueryData( + todoListOptions.queryKey, + ) // Optimistically update to the new value if (previousTodos) { - queryClient.setQueryData(todoListOptions.queryKey, { + context.client.setQueryData(todoListOptions.queryKey, { ...previousTodos, items: [ ...previousTodos.items, @@ -68,14 +70,18 @@ function Example() { return { previousTodos } }, // If the mutation fails, - // use the context returned from onMutate to roll back - onError: (err, variables, context) => { - if (context?.previousTodos) { - queryClient.setQueryData(['todos'], context.previousTodos) + // use the result returned from onMutate to roll back + onError: (err, variables, onMutateResult, context) => { + if (onMutateResult?.previousTodos) { + context.client.setQueryData( + ['todos'], + onMutateResult.previousTodos, + ) } }, // Always refetch after error or success: - onSettled: () => queryClient.invalidateQueries({ queryKey: ['todos'] }), + onSettled: (data, error, variables, onMutateResult, context) => + context.client.invalidateQueries({ queryKey: ['todos'] }), }) return ( diff --git a/packages/angular-query-experimental/src/__tests__/mutation-options.test-d.ts b/packages/angular-query-experimental/src/__tests__/mutation-options.test-d.ts index 7f802351f2..55837e61c4 100644 --- a/packages/angular-query-experimental/src/__tests__/mutation-options.test-d.ts +++ b/packages/angular-query-experimental/src/__tests__/mutation-options.test-d.ts @@ -8,6 +8,7 @@ import { } from '..' import type { DefaultError, + MutationFunctionContext, MutationState, WithRequired, } from '@tanstack/query-core' @@ -58,15 +59,39 @@ describe('mutationOptions', () => { }) }) - it('should infer context type correctly', () => { + it('should infer result type correctly', () => { mutationOptions({ mutationFn: () => Promise.resolve(5), mutationKey: ['key'], onMutate: () => { - return { name: 'context' } + return { name: 'onMutateResult' } + }, + onSuccess: (_data, _variables, onMutateResult) => { + expectTypeOf(onMutateResult).toEqualTypeOf< + { name: string } | undefined + >() + }, + }) + }) + + it('should infer context type correctly', () => { + mutationOptions({ + mutationFn: (_variables, context) => { + expectTypeOf(context).toEqualTypeOf() + return Promise.resolve(5) + }, + mutationKey: ['key'], + onMutate: (_variables, context) => { + expectTypeOf(context).toEqualTypeOf() + }, + onSuccess: (_data, _variables, _onMutateResult, context) => { + expectTypeOf(context).toEqualTypeOf() + }, + onError: (_error, _variables, _onMutateResult, context) => { + expectTypeOf(context).toEqualTypeOf() }, - onSuccess: (_data, _variables, context) => { - expectTypeOf(context).toEqualTypeOf<{ name: string }>() + onSettled: (_data, _error, _variables, _onMutateResult, context) => { + expectTypeOf(context).toEqualTypeOf() }, }) }) diff --git a/packages/angular-query-experimental/src/inject-mutation.ts b/packages/angular-query-experimental/src/inject-mutation.ts index 21ddf5ff85..97735ca4b5 100644 --- a/packages/angular-query-experimental/src/inject-mutation.ts +++ b/packages/angular-query-experimental/src/inject-mutation.ts @@ -45,16 +45,16 @@ export function injectMutation< TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, + TOnMutateResult = unknown, >( injectMutationFn: () => CreateMutationOptions< TData, TError, TVariables, - TContext + TOnMutateResult >, options?: InjectMutationOptions, -): CreateMutationResult { +): CreateMutationResult { !options?.injector && assertInInjectionContext(injectMutation) const injector = options?.injector ?? inject(Injector) const destroyRef = injector.get(DestroyRef) @@ -69,8 +69,12 @@ export function injectMutation< const optionsSignal = computed(injectMutationFn) const observerSignal = (() => { - let instance: MutationObserver | null = - null + let instance: MutationObserver< + TData, + TError, + TVariables, + TOnMutateResult + > | null = null return computed(() => { return (instance ||= new MutationObserver(queryClient, optionsSignal())) @@ -78,7 +82,7 @@ export function injectMutation< })() const mutateFnSignal = computed< - CreateMutateFunction + CreateMutateFunction >(() => { const observer = observerSignal() return (variables, mutateOptions) => { @@ -101,7 +105,7 @@ export function injectMutation< TData, TError, TVariables, - TContext + TOnMutateResult > | null>(null) effect( @@ -166,6 +170,6 @@ export function injectMutation< TData, TError, TVariables, - TContext + TOnMutateResult > } diff --git a/packages/angular-query-experimental/src/mutation-options.ts b/packages/angular-query-experimental/src/mutation-options.ts index ec702cb45c..ef0c2cc8b2 100644 --- a/packages/angular-query-experimental/src/mutation-options.ts +++ b/packages/angular-query-experimental/src/mutation-options.ts @@ -40,28 +40,28 @@ export function mutationOptions< TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, + TOnMutateResult = unknown, >( options: WithRequired< - CreateMutationOptions, + CreateMutationOptions, 'mutationKey' >, ): WithRequired< - CreateMutationOptions, + CreateMutationOptions, 'mutationKey' > export function mutationOptions< TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, + TOnMutateResult = unknown, >( options: Omit< - CreateMutationOptions, + CreateMutationOptions, 'mutationKey' >, ): Omit< - CreateMutationOptions, + CreateMutationOptions, 'mutationKey' > @@ -104,9 +104,9 @@ export function mutationOptions< TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, + TOnMutateResult = unknown, >( - options: CreateMutationOptions, -): CreateMutationOptions { + options: CreateMutationOptions, +): CreateMutationOptions { return options } diff --git a/packages/angular-query-experimental/src/types.ts b/packages/angular-query-experimental/src/types.ts index caee16822e..1d4bf86487 100644 --- a/packages/angular-query-experimental/src/types.ts +++ b/packages/angular-query-experimental/src/types.ts @@ -133,9 +133,9 @@ export interface CreateMutationOptions< TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, + TOnMutateResult = unknown, > extends OmitKeyof< - MutationObserverOptions, + MutationObserverOptions, '_defaulted' > {} @@ -143,28 +143,35 @@ export type CreateMutateFunction< TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, + TOnMutateResult = unknown, > = ( - ...args: Parameters> + ...args: Parameters< + MutateFunction + > ) => void export type CreateMutateAsyncFunction< TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, -> = MutateFunction + TOnMutateResult = unknown, +> = MutateFunction export type CreateBaseMutationResult< TData = unknown, TError = DefaultError, TVariables = unknown, - TContext = unknown, + TOnMutateResult = unknown, > = Override< - MutationObserverResult, - { mutate: CreateMutateFunction } + MutationObserverResult, + { mutate: CreateMutateFunction } > & { - mutateAsync: CreateMutateAsyncFunction + mutateAsync: CreateMutateAsyncFunction< + TData, + TError, + TVariables, + TOnMutateResult + > } type CreateStatusBasedMutationResult< @@ -172,9 +179,9 @@ type CreateStatusBasedMutationResult< TData = unknown, TError = DefaultError, TVariables = unknown, - TContext = unknown, + TOnMutateResult = unknown, > = Extract< - CreateBaseMutationResult, + CreateBaseMutationResult, { status: TStatus } > @@ -184,73 +191,73 @@ export interface BaseMutationNarrowing< TData = unknown, TError = DefaultError, TVariables = unknown, - TContext = unknown, + TOnMutateResult = unknown, > { isSuccess: SignalFunction< ( - this: CreateMutationResult, + this: CreateMutationResult, ) => this is CreateMutationResult< TData, TError, TVariables, - TContext, + TOnMutateResult, CreateStatusBasedMutationResult< 'success', TData, TError, TVariables, - TContext + TOnMutateResult > > > isError: SignalFunction< ( - this: CreateMutationResult, + this: CreateMutationResult, ) => this is CreateMutationResult< TData, TError, TVariables, - TContext, + TOnMutateResult, CreateStatusBasedMutationResult< 'error', TData, TError, TVariables, - TContext + TOnMutateResult > > > isPending: SignalFunction< ( - this: CreateMutationResult, + this: CreateMutationResult, ) => this is CreateMutationResult< TData, TError, TVariables, - TContext, + TOnMutateResult, CreateStatusBasedMutationResult< 'pending', TData, TError, TVariables, - TContext + TOnMutateResult > > > isIdle: SignalFunction< ( - this: CreateMutationResult, + this: CreateMutationResult, ) => this is CreateMutationResult< TData, TError, TVariables, - TContext, + TOnMutateResult, CreateStatusBasedMutationResult< 'idle', TData, TError, TVariables, - TContext + TOnMutateResult > > > @@ -260,13 +267,13 @@ export type CreateMutationResult< TData = unknown, TError = DefaultError, TVariables = unknown, - TContext = unknown, + TOnMutateResult = unknown, TState = CreateStatusBasedMutationResult< CreateBaseMutationResult['status'], TData, TError, TVariables, - TContext + TOnMutateResult >, -> = BaseMutationNarrowing & +> = BaseMutationNarrowing & MapToSignals> diff --git a/packages/eslint-plugin-query/src/__tests__/mutation-property-order.rule.test.ts b/packages/eslint-plugin-query/src/__tests__/mutation-property-order.rule.test.ts index 34839c684c..6668774a11 100644 --- a/packages/eslint-plugin-query/src/__tests__/mutation-property-order.rule.test.ts +++ b/packages/eslint-plugin-query/src/__tests__/mutation-property-order.rule.test.ts @@ -143,9 +143,9 @@ function getCode({ mutationFunction: mutationFunction, properties }: TestCase) { case 'onMutate': return 'onMutate: (data) => {\n return { foo: data }\n}' case 'onError': - return 'onError: (error, variables, context) => {\n console.log("error:", error, "context:", context)\n}' + return 'onError: (error, variables, onMutateResult) => {\n console.log("error:", error, "onMutateResult:", scope)\n}' case 'onSettled': - return 'onSettled: (data, error, variables, context) => {\n console.log("settled", context)\n}' + return 'onSettled: (data, error, variables, onMutateResult) => {\n console.log("settled", onMutateResult)\n}' } } return ` @@ -202,11 +202,11 @@ const regressionTestCases = { onMutate: (data) => { return { foo: data } }, - onError: (error, variables, context) => { - console.log(error, context) + onError: (error, variables, onMutateResult) => { + console.log(error, onMutateResult) }, - onSettled: (data, error, variables, context) => { - console.log('settled', context) + onSettled: (data, error, variables, onMutateResult) => { + console.log('settled', onMutateResult) }, }) `, diff --git a/packages/query-core/src/__tests__/hydration.test.tsx b/packages/query-core/src/__tests__/hydration.test.tsx index 30a4b9a985..46945e1b89 100644 --- a/packages/query-core/src/__tests__/hydration.test.tsx +++ b/packages/query-core/src/__tests__/hydration.test.tsx @@ -545,6 +545,7 @@ describe('dehydration and rehydration', () => { { id: 2, text: 'text' }, { text: 'text' }, { optimisticTodo: { id: 1, text: 'text' } }, + { client: client, meta: undefined, mutationKey: ['addTodo'] }, ) client.clear() diff --git a/packages/query-core/src/__tests__/mutationCache.test.tsx b/packages/query-core/src/__tests__/mutationCache.test.tsx index 8759a7ca98..e52498c64f 100644 --- a/packages/query-core/src/__tests__/mutationCache.test.tsx +++ b/packages/query-core/src/__tests__/mutationCache.test.tsx @@ -27,7 +27,7 @@ describe('mutationCache', () => { mutationKey: key, mutationFn: () => sleep(10).then(() => Promise.reject(new Error('error'))), - onMutate: () => 'context', + onMutate: () => 'result', }, 'vars', ).catch(() => undefined) @@ -39,8 +39,13 @@ describe('mutationCache', () => { expect(onError).toHaveBeenCalledWith( new Error('error'), 'vars', - 'context', + 'result', mutation, + { + client: testClient, + meta: undefined, + mutationKey: key, + }, ) expect(onSuccess).not.toHaveBeenCalled() expect(onSettled).toHaveBeenCalledTimes(1) @@ -48,8 +53,13 @@ describe('mutationCache', () => { undefined, new Error('error'), 'vars', - 'context', + 'result', mutation, + { + client: testClient, + meta: undefined, + mutationKey: key, + }, ) }) @@ -109,7 +119,7 @@ describe('mutationCache', () => { { mutationKey: key, mutationFn: () => sleep(10).then(() => ({ data: 5 })), - onMutate: () => 'context', + onMutate: () => 'result', }, 'vars', ) @@ -121,8 +131,13 @@ describe('mutationCache', () => { expect(onSuccess).toHaveBeenCalledWith( { data: 5 }, 'vars', - 'context', + 'result', mutation, + { + client: testClient, + meta: undefined, + mutationKey: key, + }, ) expect(onError).not.toHaveBeenCalled() expect(onSettled).toHaveBeenCalledTimes(1) @@ -130,8 +145,13 @@ describe('mutationCache', () => { { data: 5 }, null, 'vars', - 'context', + 'result', mutation, + { + client: testClient, + meta: undefined, + mutationKey: key, + }, ) }) @@ -187,14 +207,18 @@ describe('mutationCache', () => { { mutationKey: key, mutationFn: () => sleep(10).then(() => ({ data: 5 })), - onMutate: () => 'context', + onMutate: () => 'result', }, 'vars', ) const mutation = testCache.getAll()[0] - expect(onMutate).toHaveBeenCalledWith('vars', mutation) + expect(onMutate).toHaveBeenCalledWith('vars', mutation, { + client: testClient, + meta: undefined, + mutationKey: key, + }) }) test('should be awaited', async () => { diff --git a/packages/query-core/src/__tests__/mutationObserver.test.tsx b/packages/query-core/src/__tests__/mutationObserver.test.tsx index f7e9a8839e..995156be41 100644 --- a/packages/query-core/src/__tests__/mutationObserver.test.tsx +++ b/packages/query-core/src/__tests__/mutationObserver.test.tsx @@ -320,13 +320,22 @@ describe('mutationObserver', () => { await vi.advanceTimersByTimeAsync(0) expect(onSuccess).toHaveBeenCalledTimes(1) - expect(onSuccess).toHaveBeenCalledWith('SUCCESS', 'success', undefined) + expect(onSuccess).toHaveBeenCalledWith('SUCCESS', 'success', undefined, { + client: queryClient, + meta: undefined, + mutationKey: undefined, + }) expect(onSettled).toHaveBeenCalledTimes(1) expect(onSettled).toHaveBeenCalledWith( 'SUCCESS', null, 'success', undefined, + { + client: queryClient, + meta: undefined, + mutationKey: undefined, + }, ) unsubscribe() @@ -354,9 +363,23 @@ describe('mutationObserver', () => { await vi.advanceTimersByTimeAsync(0) expect(onError).toHaveBeenCalledTimes(1) - expect(onError).toHaveBeenCalledWith(error, 'error', undefined) + expect(onError).toHaveBeenCalledWith(error, 'error', undefined, { + client: queryClient, + meta: undefined, + mutationKey: undefined, + }) expect(onSettled).toHaveBeenCalledTimes(1) - expect(onSettled).toHaveBeenCalledWith(undefined, error, 'error', undefined) + expect(onSettled).toHaveBeenCalledWith( + undefined, + error, + 'error', + undefined, + { + client: queryClient, + meta: undefined, + mutationKey: undefined, + }, + ) unsubscribe() }) diff --git a/packages/query-core/src/__tests__/mutations.test.tsx b/packages/query-core/src/__tests__/mutations.test.tsx index 4201972c88..cb7cd79d3a 100644 --- a/packages/query-core/src/__tests__/mutations.test.tsx +++ b/packages/query-core/src/__tests__/mutations.test.tsx @@ -53,7 +53,11 @@ describe('mutations', () => { await vi.advanceTimersByTimeAsync(0) expect(fn).toHaveBeenCalledTimes(1) - expect(fn).toHaveBeenCalledWith('vars') + expect(fn).toHaveBeenCalledWith('vars', { + client: queryClient, + meta: undefined, + mutationKey: key, + }) }) test('mutation should set correct success states', async () => { @@ -632,7 +636,7 @@ describe('mutations', () => { mutationFn: () => Promise.resolve('success'), onMutate: () => { results.push('onMutate-sync') - return { backup: 'data' } // onMutate can return context + return { backup: 'data' } // onMutate can return a result }, onSuccess: () => { results.push('onSuccess-implicit-void') @@ -760,8 +764,8 @@ describe('mutations', () => { results.push('sync-onError') return Promise.resolve('error-return-ignored') }, - onSettled: (_data, _error, _variables, context) => { - results.push(`settled-context-${context?.rollback}`) + onSettled: (_data, _error, _variables, onMutateResult) => { + results.push(`settled-onMutateResult-${onMutateResult?.rollback}`) return Promise.all([ Promise.resolve('cleanup-1'), Promise.resolve('cleanup-2'), @@ -781,7 +785,7 @@ describe('mutations', () => { expect(results).toEqual([ 'sync-onMutate', 'async-onSuccess', - 'settled-context-data', + 'settled-onMutateResult-data', ]) }) @@ -812,8 +816,8 @@ describe('mutations', () => { sleep(20).then(() => results.push('error-cleanup-2')), ]) }, - onSettled: (_data, _error, _variables, context) => { - results.push(`settled-error-${context?.backup}`) + onSettled: (_data, _error, _variables, onMutateResult) => { + results.push(`settled-error-${onMutateResult?.backup}`) return Promise.allSettled([ Promise.resolve('settled-cleanup'), Promise.reject('settled-error'), diff --git a/packages/query-core/src/__tests__/utils.test.tsx b/packages/query-core/src/__tests__/utils.test.tsx index 414b892447..9cee1b4642 100644 --- a/packages/query-core/src/__tests__/utils.test.tsx +++ b/packages/query-core/src/__tests__/utils.test.tsx @@ -423,6 +423,7 @@ describe('core/utils', () => { const filters = { mutationKey: ['key1'] } const queryClient = new QueryClient() const mutation = new Mutation({ + client: queryClient, mutationId: 1, mutationCache: queryClient.getMutationCache(), options: {}, diff --git a/packages/query-core/src/mutation.ts b/packages/query-core/src/mutation.ts index 3aced112e3..4d04626088 100644 --- a/packages/query-core/src/mutation.ts +++ b/packages/query-core/src/mutation.ts @@ -3,6 +3,7 @@ import { Removable } from './removable' import { createRetryer } from './retryer' import type { DefaultError, + MutationFunctionContext, MutationMeta, MutationOptions, MutationStatus, @@ -10,23 +11,25 @@ import type { import type { MutationCache } from './mutationCache' import type { MutationObserver } from './mutationObserver' import type { Retryer } from './retryer' +import type { QueryClient } from './queryClient' // TYPES -interface MutationConfig { +interface MutationConfig { + client: QueryClient mutationId: number mutationCache: MutationCache - options: MutationOptions - state?: MutationState + options: MutationOptions + state?: MutationState } export interface MutationState< TData = unknown, TError = DefaultError, TVariables = unknown, - TContext = unknown, + TOnMutateResult = unknown, > { - context: TContext | undefined + context: TOnMutateResult | undefined data: TData | undefined error: TError | null failureCount: number @@ -43,11 +46,11 @@ interface FailedAction { error: TError | null } -interface PendingAction { +interface PendingAction { type: 'pending' isPaused: boolean variables?: TVariables - context?: TContext + context?: TOnMutateResult } interface SuccessAction { @@ -68,11 +71,11 @@ interface ContinueAction { type: 'continue' } -export type Action = +export type Action = | ContinueAction | ErrorAction | FailedAction - | PendingAction + | PendingAction | PauseAction | SuccessAction @@ -82,19 +85,25 @@ export class Mutation< TData = unknown, TError = DefaultError, TVariables = unknown, - TContext = unknown, + TOnMutateResult = unknown, > extends Removable { - state: MutationState - options!: MutationOptions + state: MutationState + options!: MutationOptions readonly mutationId: number - #observers: Array> + #client: QueryClient + #observers: Array< + MutationObserver + > #mutationCache: MutationCache #retryer?: Retryer - constructor(config: MutationConfig) { + constructor( + config: MutationConfig, + ) { super() + this.#client = config.client this.mutationId = config.mutationId this.#mutationCache = config.mutationCache this.#observers = [] @@ -105,7 +114,7 @@ export class Mutation< } setOptions( - options: MutationOptions, + options: MutationOptions, ): void { this.options = options @@ -166,12 +175,19 @@ export class Mutation< this.#dispatch({ type: 'continue' }) } + const mutationFnContext = { + client: this.#client, + meta: this.options.meta, + mutationKey: this.options.mutationKey, + } satisfies MutationFunctionContext + this.#retryer = createRetryer({ fn: () => { if (!this.options.mutationFn) { return Promise.reject(new Error('No mutationFn found')) } - return this.options.mutationFn(variables) + + return this.options.mutationFn(variables, mutationFnContext) }, onFail: (failureCount, error) => { this.#dispatch({ type: 'failed', failureCount, error }) @@ -199,8 +215,12 @@ export class Mutation< await this.#mutationCache.config.onMutate?.( variables, this as Mutation, + mutationFnContext, + ) + const context = await this.options.onMutate?.( + variables, + mutationFnContext, ) - const context = await this.options.onMutate?.(variables) if (context !== this.state.context) { this.#dispatch({ type: 'pending', @@ -218,9 +238,15 @@ export class Mutation< variables, this.state.context, this as Mutation, + mutationFnContext, ) - await this.options.onSuccess?.(data, variables, this.state.context!) + await this.options.onSuccess?.( + data, + variables, + this.state.context, + mutationFnContext, + ) // Notify cache callback await this.#mutationCache.config.onSettled?.( @@ -229,9 +255,16 @@ export class Mutation< this.state.variables, this.state.context, this as Mutation, + mutationFnContext, ) - await this.options.onSettled?.(data, null, variables, this.state.context) + await this.options.onSettled?.( + data, + null, + variables, + this.state.context, + mutationFnContext, + ) this.#dispatch({ type: 'success', data }) return data @@ -243,12 +276,14 @@ export class Mutation< variables, this.state.context, this as Mutation, + mutationFnContext, ) await this.options.onError?.( error as TError, variables, this.state.context, + mutationFnContext, ) // Notify cache callback @@ -258,6 +293,7 @@ export class Mutation< this.state.variables, this.state.context, this as Mutation, + mutationFnContext, ) await this.options.onSettled?.( @@ -265,6 +301,7 @@ export class Mutation< error as TError, variables, this.state.context, + mutationFnContext, ) throw error } finally { @@ -275,10 +312,10 @@ export class Mutation< } } - #dispatch(action: Action): void { + #dispatch(action: Action): void { const reducer = ( - state: MutationState, - ): MutationState => { + state: MutationState, + ): MutationState => { switch (action.type) { case 'failed': return { @@ -350,8 +387,8 @@ export function getDefaultState< TData, TError, TVariables, - TContext, ->(): MutationState { + TOnMutateResult, +>(): MutationState { return { context: undefined, data: undefined, diff --git a/packages/query-core/src/mutationCache.ts b/packages/query-core/src/mutationCache.ts index 6ab95fbfed..d204c904e5 100644 --- a/packages/query-core/src/mutationCache.ts +++ b/packages/query-core/src/mutationCache.ts @@ -3,7 +3,12 @@ import { Mutation } from './mutation' import { matchMutation, noop } from './utils' import { Subscribable } from './subscribable' import type { MutationObserver } from './mutationObserver' -import type { DefaultError, MutationOptions, NotifyEvent } from './types' +import type { + DefaultError, + MutationFunctionContext, + MutationOptions, + NotifyEvent, +} from './types' import type { QueryClient } from './queryClient' import type { Action, MutationState } from './mutation' import type { MutationFilters } from './utils' @@ -14,25 +19,29 @@ interface MutationCacheConfig { onError?: ( error: DefaultError, variables: unknown, - context: unknown, + onMutateResult: unknown, mutation: Mutation, + context: MutationFunctionContext, ) => Promise | unknown onSuccess?: ( data: unknown, variables: unknown, - context: unknown, + onMutateResult: unknown, mutation: Mutation, + context: MutationFunctionContext, ) => Promise | unknown onMutate?: ( variables: unknown, mutation: Mutation, + context: MutationFunctionContext, ) => Promise | unknown onSettled?: ( data: unknown | undefined, error: DefaultError | null, variables: unknown, - context: unknown, + onMutateResult: unknown, mutation: Mutation, + context: MutationFunctionContext, ) => Promise | unknown } @@ -93,12 +102,13 @@ export class MutationCache extends Subscribable { this.#mutationId = 0 } - build( + build( client: QueryClient, - options: MutationOptions, - state?: MutationState, - ): Mutation { + options: MutationOptions, + state?: MutationState, + ): Mutation { const mutation = new Mutation({ + client, mutationCache: this, mutationId: ++this.#mutationId, options: client.defaultMutationOptions(options), @@ -195,15 +205,15 @@ export class MutationCache extends Subscribable { TData = unknown, TError = DefaultError, TVariables = any, - TContext = unknown, + TOnMutateResult = unknown, >( filters: MutationFilters, - ): Mutation | undefined { + ): Mutation | undefined { const defaultedFilters = { exact: true, ...filters } return this.getAll().find((mutation) => matchMutation(defaultedFilters, mutation), - ) as Mutation | undefined + ) as Mutation | undefined } findAll(filters: MutationFilters = {}): Array { diff --git a/packages/query-core/src/mutationObserver.ts b/packages/query-core/src/mutationObserver.ts index 1178bbdf75..abb2a5389c 100644 --- a/packages/query-core/src/mutationObserver.ts +++ b/packages/query-core/src/mutationObserver.ts @@ -6,6 +6,7 @@ import type { QueryClient } from './queryClient' import type { DefaultError, MutateOptions, + MutationFunctionContext, MutationObserverOptions, MutationObserverResult, } from './types' @@ -13,8 +14,8 @@ import type { Action, Mutation } from './mutation' // TYPES -type MutationObserverListener = ( - result: MutationObserverResult, +type MutationObserverListener = ( + result: MutationObserverResult, ) => void // CLASS @@ -23,21 +24,30 @@ export class MutationObserver< TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, + TOnMutateResult = unknown, > extends Subscribable< - MutationObserverListener + MutationObserverListener > { - options!: MutationObserverOptions + options!: MutationObserverOptions #client: QueryClient - #currentResult: MutationObserverResult = - undefined! - #currentMutation?: Mutation - #mutateOptions?: MutateOptions + #currentResult: MutationObserverResult< + TData, + TError, + TVariables, + TOnMutateResult + > = undefined! + #currentMutation?: Mutation + #mutateOptions?: MutateOptions constructor( client: QueryClient, - options: MutationObserverOptions, + options: MutationObserverOptions< + TData, + TError, + TVariables, + TOnMutateResult + >, ) { super() @@ -53,10 +63,15 @@ export class MutationObserver< } setOptions( - options: MutationObserverOptions, + options: MutationObserverOptions< + TData, + TError, + TVariables, + TOnMutateResult + >, ) { const prevOptions = this.options as - | MutationObserverOptions + | MutationObserverOptions | undefined this.options = this.#client.defaultMutationOptions(options) if (!shallowEqualObjects(this.options, prevOptions)) { @@ -84,7 +99,9 @@ export class MutationObserver< } } - onMutationUpdate(action: Action): void { + onMutationUpdate( + action: Action, + ): void { this.#updateResult() this.#notify(action) @@ -94,7 +111,7 @@ export class MutationObserver< TData, TError, TVariables, - TContext + TOnMutateResult > { return this.#currentResult } @@ -110,7 +127,7 @@ export class MutationObserver< mutate( variables: TVariables, - options?: MutateOptions, + options?: MutateOptions, ): Promise { this.#mutateOptions = options @@ -128,7 +145,7 @@ export class MutationObserver< #updateResult(): void { const state = this.#currentMutation?.state ?? - getDefaultState() + getDefaultState() this.#currentResult = { ...state, @@ -138,25 +155,48 @@ export class MutationObserver< isIdle: state.status === 'idle', mutate: this.mutate, reset: this.reset, - } as MutationObserverResult + } as MutationObserverResult } - #notify(action?: Action): void { + #notify(action?: Action): void { notifyManager.batch(() => { // First trigger the mutate callbacks if (this.#mutateOptions && this.hasListeners()) { const variables = this.#currentResult.variables! - const context = this.#currentResult.context + const onMutateResult = this.#currentResult.context + + const context = { + client: this.#client, + meta: this.options.meta, + mutationKey: this.options.mutationKey, + } satisfies MutationFunctionContext if (action?.type === 'success') { - this.#mutateOptions.onSuccess?.(action.data, variables, context!) - this.#mutateOptions.onSettled?.(action.data, null, variables, context) + this.#mutateOptions.onSuccess?.( + action.data, + variables, + onMutateResult, + context, + ) + this.#mutateOptions.onSettled?.( + action.data, + null, + variables, + onMutateResult, + context, + ) } else if (action?.type === 'error') { - this.#mutateOptions.onError?.(action.error, variables, context) + this.#mutateOptions.onError?.( + action.error, + variables, + onMutateResult, + context, + ) this.#mutateOptions.onSettled?.( undefined, action.error, variables, + onMutateResult, context, ) } diff --git a/packages/query-core/src/queryClient.ts b/packages/query-core/src/queryClient.ts index 5154ecb535..80cc36668a 100644 --- a/packages/query-core/src/queryClient.ts +++ b/packages/query-core/src/queryClient.ts @@ -512,11 +512,11 @@ export class QueryClient { TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, + TOnMutateResult = unknown, >( mutationKey: MutationKey, options: OmitKeyof< - MutationObserverOptions, + MutationObserverOptions, 'mutationKey' >, ): void { diff --git a/packages/query-core/src/types.ts b/packages/query-core/src/types.ts index df6ea8c173..54c1764477 100644 --- a/packages/query-core/src/types.ts +++ b/packages/query-core/src/types.ts @@ -1091,36 +1091,47 @@ export type MutationMeta = Register extends { : Record : Record +export type MutationFunctionContext = { + client: QueryClient + meta: MutationMeta | undefined + mutationKey?: MutationKey +} + export type MutationFunction = ( variables: TVariables, + context: MutationFunctionContext, ) => Promise export interface MutationOptions< TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, + TOnMutateResult = unknown, > { mutationFn?: MutationFunction mutationKey?: MutationKey onMutate?: ( variables: TVariables, - ) => Promise | TContext | undefined + context: MutationFunctionContext, + ) => Promise | TOnMutateResult | undefined onSuccess?: ( data: TData, variables: TVariables, - context: TContext, + onMutateResult: TOnMutateResult | undefined, + context: MutationFunctionContext, ) => Promise | unknown onError?: ( error: TError, variables: TVariables, - context: TContext | undefined, + onMutateResult: TOnMutateResult | undefined, + context: MutationFunctionContext, ) => Promise | unknown onSettled?: ( data: TData | undefined, error: TError | null, variables: TVariables, - context: TContext | undefined, + onMutateResult: TOnMutateResult | undefined, + context: MutationFunctionContext, ) => Promise | unknown retry?: RetryValue retryDelay?: RetryDelayValue @@ -1135,8 +1146,8 @@ export interface MutationObserverOptions< TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, -> extends MutationOptions { + TOnMutateResult = unknown, +> extends MutationOptions { throwOnError?: boolean | ((error: TError) => boolean) } @@ -1144,19 +1155,26 @@ export interface MutateOptions< TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, + TOnMutateResult = unknown, > { - onSuccess?: (data: TData, variables: TVariables, context: TContext) => void + onSuccess?: ( + data: TData, + variables: TVariables, + onMutateResult: TOnMutateResult | undefined, + context: MutationFunctionContext, + ) => void onError?: ( error: TError, variables: TVariables, - context: TContext | undefined, + onMutateResult: TOnMutateResult | undefined, + context: MutationFunctionContext, ) => void onSettled?: ( data: TData | undefined, error: TError | null, variables: TVariables, - context: TContext | undefined, + onMutateResult: TOnMutateResult | undefined, + context: MutationFunctionContext, ) => void } @@ -1164,18 +1182,18 @@ export type MutateFunction< TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, + TOnMutateResult = unknown, > = ( variables: TVariables, - options?: MutateOptions, + options?: MutateOptions, ) => Promise export interface MutationObserverBaseResult< TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, -> extends MutationState { + TOnMutateResult = unknown, +> extends MutationState { /** * The last successfully resolved data for the mutation. */ @@ -1228,7 +1246,7 @@ export interface MutationObserverBaseResult< * - If you make multiple requests, `onSuccess` will fire only after the latest call you've made. * - All the callback functions (`onSuccess`, `onError`, `onSettled`) are void functions, and the returned value will be ignored. */ - mutate: MutateFunction + mutate: MutateFunction /** * A function to clean the mutation internal state (i.e., it resets the mutation to its initial state). */ @@ -1239,8 +1257,13 @@ export interface MutationObserverIdleResult< TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, -> extends MutationObserverBaseResult { + TOnMutateResult = unknown, +> extends MutationObserverBaseResult< + TData, + TError, + TVariables, + TOnMutateResult + > { data: undefined variables: undefined error: null @@ -1255,8 +1278,13 @@ export interface MutationObserverLoadingResult< TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, -> extends MutationObserverBaseResult { + TOnMutateResult = unknown, +> extends MutationObserverBaseResult< + TData, + TError, + TVariables, + TOnMutateResult + > { data: undefined variables: TVariables error: null @@ -1271,8 +1299,13 @@ export interface MutationObserverErrorResult< TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, -> extends MutationObserverBaseResult { + TOnMutateResult = unknown, +> extends MutationObserverBaseResult< + TData, + TError, + TVariables, + TOnMutateResult + > { data: undefined error: TError variables: TVariables @@ -1287,8 +1320,13 @@ export interface MutationObserverSuccessResult< TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, -> extends MutationObserverBaseResult { + TOnMutateResult = unknown, +> extends MutationObserverBaseResult< + TData, + TError, + TVariables, + TOnMutateResult + > { data: TData error: null variables: TVariables @@ -1303,12 +1341,12 @@ export type MutationObserverResult< TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, + TOnMutateResult = unknown, > = - | MutationObserverIdleResult - | MutationObserverLoadingResult - | MutationObserverErrorResult - | MutationObserverSuccessResult + | MutationObserverIdleResult + | MutationObserverLoadingResult + | MutationObserverErrorResult + | MutationObserverSuccessResult export interface QueryClientConfig { queryCache?: QueryCache diff --git a/packages/query-core/src/utils.ts b/packages/query-core/src/utils.ts index b4da471a9b..2b46cc1c74 100644 --- a/packages/query-core/src/utils.ts +++ b/packages/query-core/src/utils.ts @@ -47,7 +47,7 @@ export interface MutationFilters< TData = unknown, TError = DefaultError, TVariables = unknown, - TContext = unknown, + TOnMutateResult = unknown, > { /** * Match mutation key exactly @@ -57,7 +57,7 @@ export interface MutationFilters< * Include mutations matching this predicate function */ predicate?: ( - mutation: Mutation, + mutation: Mutation, ) => boolean /** * Include mutations matching this mutation key diff --git a/packages/react-query/src/__tests__/mutationOptions.test-d.tsx b/packages/react-query/src/__tests__/mutationOptions.test-d.tsx index 21373fec31..c411fe9dff 100644 --- a/packages/react-query/src/__tests__/mutationOptions.test-d.tsx +++ b/packages/react-query/src/__tests__/mutationOptions.test-d.tsx @@ -4,6 +4,7 @@ import { useIsMutating, useMutation, useMutationState } from '..' import { mutationOptions } from '../mutationOptions' import type { DefaultError, + MutationFunctionContext, MutationState, WithRequired, } from '@tanstack/query-core' @@ -54,15 +55,39 @@ describe('mutationOptions', () => { }) }) - it('should infer context type correctly', () => { + it('should infer result type correctly', () => { mutationOptions({ mutationFn: () => Promise.resolve(5), mutationKey: ['key'], onMutate: () => { - return { name: 'context' } + return { name: 'onMutateResult' } + }, + onSuccess: (_data, _variables, onMutateResult) => { + expectTypeOf(onMutateResult).toEqualTypeOf< + { name: string } | undefined + >() + }, + }) + }) + + it('should infer context type correctly', () => { + mutationOptions({ + mutationFn: (_variables, context) => { + expectTypeOf(context).toEqualTypeOf() + return Promise.resolve(5) + }, + mutationKey: ['key'], + onMutate: (_variables, context) => { + expectTypeOf(context).toEqualTypeOf() + }, + onSuccess: (_data, _variables, _onMutateResult, context) => { + expectTypeOf(context).toEqualTypeOf() + }, + onError: (_error, _variables, _onMutateResult, context) => { + expectTypeOf(context).toEqualTypeOf() }, - onSuccess: (_data, _variables, context) => { - expectTypeOf(context).toEqualTypeOf<{ name: string }>() + onSettled: (_data, _error, _variables, _onMutateResult, context) => { + expectTypeOf(context).toEqualTypeOf() }, }) }) diff --git a/packages/react-query/src/__tests__/useMutation.test.tsx b/packages/react-query/src/__tests__/useMutation.test.tsx index 5ea9a53a4f..30800c9b08 100644 --- a/packages/react-query/src/__tests__/useMutation.test.tsx +++ b/packages/react-query/src/__tests__/useMutation.test.tsx @@ -525,7 +525,11 @@ describe('useMutation', () => { ).toBeInTheDocument() expect(onMutate).toHaveBeenCalledTimes(1) - expect(onMutate).toHaveBeenCalledWith('todo') + expect(onMutate).toHaveBeenCalledWith('todo', { + client: queryClient, + meta: undefined, + mutationKey: undefined, + }) onlineMock.mockReturnValue(true) queryClient.getMutationCache().resumePausedMutations() @@ -979,12 +983,22 @@ describe('useMutation', () => { 'result-todo1', 'todo1', undefined, + { + client: queryClient, + meta: undefined, + mutationKey: undefined, + }, ) expect(onSuccess).toHaveBeenNthCalledWith( 2, 'result-todo2', 'todo2', undefined, + { + client: queryClient, + meta: undefined, + mutationKey: undefined, + }, ) expect(onSettled).toHaveBeenCalledTimes(2) expect(onSuccessMutate).toHaveBeenCalledTimes(1) @@ -992,6 +1006,11 @@ describe('useMutation', () => { 'result-todo2', 'todo2', undefined, + { + client: queryClient, + meta: undefined, + mutationKey: undefined, + }, ) expect(onSettledMutate).toHaveBeenCalledTimes(1) expect(onSettledMutate).toHaveBeenCalledWith( @@ -999,6 +1018,11 @@ describe('useMutation', () => { null, 'todo2', undefined, + { + client: queryClient, + meta: undefined, + mutationKey: undefined, + }, ) }) @@ -1033,7 +1057,11 @@ describe('useMutation', () => { await vi.advanceTimersByTimeAsync(11) expect(rendered.getByText('status: error')).toBeInTheDocument() - expect(onError).toHaveBeenCalledWith(error, 'todo', undefined) + expect(onError).toHaveBeenCalledWith(error, 'todo', undefined, { + client: queryClient, + meta: undefined, + mutationKey: undefined, + }) }) it('should go to error state if onError callback errors', async () => { @@ -1110,7 +1138,11 @@ describe('useMutation', () => { expect( rendered.getByText('error: mutateFnError, status: error'), ).toBeInTheDocument() - expect(onError).toHaveBeenCalledWith(mutateFnError, 'todo', undefined) + expect(onError).toHaveBeenCalledWith(mutateFnError, 'todo', undefined, { + client: queryClient, + meta: undefined, + mutationKey: undefined, + }) }) it('should use provided custom queryClient', async () => { diff --git a/packages/react-query/src/mutationOptions.ts b/packages/react-query/src/mutationOptions.ts index ffca2383aa..1ee3110b19 100644 --- a/packages/react-query/src/mutationOptions.ts +++ b/packages/react-query/src/mutationOptions.ts @@ -5,34 +5,37 @@ export function mutationOptions< TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, + TOnMutateResult = unknown, >( options: WithRequired< - UseMutationOptions, + UseMutationOptions, 'mutationKey' >, ): WithRequired< - UseMutationOptions, + UseMutationOptions, 'mutationKey' > export function mutationOptions< TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, + TOnMutateResult = unknown, >( options: Omit< - UseMutationOptions, + UseMutationOptions, 'mutationKey' >, -): Omit, 'mutationKey'> +): Omit< + UseMutationOptions, + 'mutationKey' +> export function mutationOptions< TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, + TOnMutateResult = unknown, >( - options: UseMutationOptions, -): UseMutationOptions { + options: UseMutationOptions, +): UseMutationOptions { return options } diff --git a/packages/react-query/src/types.ts b/packages/react-query/src/types.ts index 08c5e840ca..2c52ac5b65 100644 --- a/packages/react-query/src/types.ts +++ b/packages/react-query/src/types.ts @@ -193,9 +193,9 @@ export interface UseMutationOptions< TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, + TOnMutateResult = unknown, > extends OmitKeyof< - MutationObserverOptions, + MutationObserverOptions, '_defaulted' > {} @@ -203,31 +203,40 @@ export type UseMutateFunction< TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, + TOnMutateResult = unknown, > = ( - ...args: Parameters> + ...args: Parameters< + MutateFunction + > ) => void export type UseMutateAsyncFunction< TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, -> = MutateFunction + TOnMutateResult = unknown, +> = MutateFunction export type UseBaseMutationResult< TData = unknown, TError = DefaultError, TVariables = unknown, - TContext = unknown, + TOnMutateResult = unknown, > = Override< - MutationObserverResult, - { mutate: UseMutateFunction } -> & { mutateAsync: UseMutateAsyncFunction } + MutationObserverResult, + { mutate: UseMutateFunction } +> & { + mutateAsync: UseMutateAsyncFunction< + TData, + TError, + TVariables, + TOnMutateResult + > +} export type UseMutationResult< TData = unknown, TError = DefaultError, TVariables = unknown, - TContext = unknown, -> = UseBaseMutationResult + TOnMutateResult = unknown, +> = UseBaseMutationResult diff --git a/packages/react-query/src/useMutation.ts b/packages/react-query/src/useMutation.ts index 6847c0d2e3..2c66eb8ba8 100644 --- a/packages/react-query/src/useMutation.ts +++ b/packages/react-query/src/useMutation.ts @@ -20,16 +20,16 @@ export function useMutation< TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, + TOnMutateResult = unknown, >( - options: UseMutationOptions, + options: UseMutationOptions, queryClient?: QueryClient, -): UseMutationResult { +): UseMutationResult { const client = useQueryClient(queryClient) const [observer] = React.useState( () => - new MutationObserver( + new MutationObserver( client, options, ), @@ -50,7 +50,7 @@ export function useMutation< ) const mutate = React.useCallback< - UseMutateFunction + UseMutateFunction >( (variables, mutateOptions) => { observer.mutate(variables, mutateOptions).catch(noop) diff --git a/packages/solid-query/src/__tests__/useMutation.test.tsx b/packages/solid-query/src/__tests__/useMutation.test.tsx index 8dfa11c9df..2ad4008191 100644 --- a/packages/solid-query/src/__tests__/useMutation.test.tsx +++ b/packages/solid-query/src/__tests__/useMutation.test.tsx @@ -583,7 +583,11 @@ describe('useMutation', () => { ).toBeInTheDocument() expect(onMutate).toHaveBeenCalledTimes(1) - expect(onMutate).toHaveBeenCalledWith('todo') + expect(onMutate).toHaveBeenCalledWith('todo', { + client: queryClient, + meta: undefined, + mutationKey: undefined, + }) onlineMock.mockRestore() window.dispatchEvent(new Event('online')) @@ -1053,13 +1057,22 @@ describe('useMutation', () => { expect(onSuccess).toHaveBeenCalledTimes(2) expect(onSettled).toHaveBeenCalledTimes(2) expect(onSuccessMutate).toHaveBeenCalledTimes(1) - expect(onSuccessMutate).toHaveBeenCalledWith('result2', 'todo', undefined) + expect(onSuccessMutate).toHaveBeenCalledWith('result2', 'todo', undefined, { + client: queryClient, + meta: undefined, + mutationKey: undefined, + }) expect(onSettledMutate).toHaveBeenCalledTimes(1) expect(onSettledMutate).toHaveBeenCalledWith( 'result2', null, 'todo', undefined, + { + client: queryClient, + meta: undefined, + mutationKey: undefined, + }, ) }) @@ -1098,7 +1111,11 @@ describe('useMutation', () => { await vi.advanceTimersByTimeAsync(10) await rendered.findByText('status: error') - expect(onError).toHaveBeenCalledWith(error, 'todo', undefined) + expect(onError).toHaveBeenCalledWith(error, 'todo', undefined, { + client: queryClient, + meta: undefined, + mutationKey: undefined, + }) }) it('should go to error state if onError callback errors', async () => { @@ -1183,7 +1200,11 @@ describe('useMutation', () => { rendered.getByText('error: mutateFnError, status: error'), ).toBeInTheDocument() - expect(onError).toHaveBeenCalledWith(mutateFnError, 'todo', undefined) + expect(onError).toHaveBeenCalledWith(mutateFnError, 'todo', undefined, { + client: queryClient, + meta: undefined, + mutationKey: undefined, + }) }) it('should use provided custom queryClient', async () => { diff --git a/packages/solid-query/src/types.ts b/packages/solid-query/src/types.ts index 2d6876a677..2f52caaafc 100644 --- a/packages/solid-query/src/types.ts +++ b/packages/solid-query/src/types.ts @@ -144,9 +144,9 @@ export interface SolidMutationOptions< TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, + TOnMutateResult = unknown, > extends OmitKeyof< - MutationObserverOptions, + MutationObserverOptions, '_defaulted' > {} @@ -154,40 +154,47 @@ export type UseMutationOptions< TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, -> = Accessor> + TOnMutateResult = unknown, +> = Accessor> export type UseMutateFunction< TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, + TOnMutateResult = unknown, > = ( - ...args: Parameters> + ...args: Parameters< + MutateFunction + > ) => void export type UseMutateAsyncFunction< TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, -> = MutateFunction + TOnMutateResult = unknown, +> = MutateFunction export type UseBaseMutationResult< TData = unknown, TError = DefaultError, TVariables = unknown, - TContext = unknown, + TOnMutateResult = unknown, > = Override< - MutationObserverResult, - { mutate: UseMutateFunction } + MutationObserverResult, + { mutate: UseMutateFunction } > & { - mutateAsync: UseMutateAsyncFunction + mutateAsync: UseMutateAsyncFunction< + TData, + TError, + TVariables, + TOnMutateResult + > } export type UseMutationResult< TData = unknown, TError = DefaultError, TVariables = unknown, - TContext = unknown, -> = UseBaseMutationResult + TOnMutateResult = unknown, +> = UseBaseMutationResult diff --git a/packages/solid-query/src/useMutation.ts b/packages/solid-query/src/useMutation.ts index df114395c7..056766a643 100644 --- a/packages/solid-query/src/useMutation.ts +++ b/packages/solid-query/src/useMutation.ts @@ -16,27 +16,31 @@ export function useMutation< TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, + TOnMutateResult = unknown, >( - options: UseMutationOptions, + options: UseMutationOptions, queryClient?: Accessor, -): UseMutationResult { +): UseMutationResult { const client = createMemo(() => useQueryClient(queryClient?.())) - const observer = new MutationObserver( - client(), - options(), - ) + const observer = new MutationObserver< + TData, + TError, + TVariables, + TOnMutateResult + >(client(), options()) - const mutate: UseMutateFunction = ( - variables, - mutateOptions, - ) => { + const mutate: UseMutateFunction< + TData, + TError, + TVariables, + TOnMutateResult + > = (variables, mutateOptions) => { observer.mutate(variables, mutateOptions).catch(noop) } const [state, setState] = createStore< - UseMutationResult + UseMutationResult >({ ...observer.getCurrentResult(), mutate, diff --git a/packages/svelte-query/src/createMutation.ts b/packages/svelte-query/src/createMutation.ts index a08dcf91fa..698ea75ac7 100644 --- a/packages/svelte-query/src/createMutation.ts +++ b/packages/svelte-query/src/createMutation.ts @@ -14,22 +14,24 @@ export function createMutation< TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, + TOnMutateResult = unknown, >( options: StoreOrVal< - CreateMutationOptions + CreateMutationOptions >, queryClient?: QueryClient, -): CreateMutationResult { +): CreateMutationResult { const client = useQueryClient(queryClient) const optionsStore = isSvelteStore(options) ? options : readable(options) - const observer = new MutationObserver( - client, - get(optionsStore), - ) - let mutate: CreateMutateFunction + const observer = new MutationObserver< + TData, + TError, + TVariables, + TOnMutateResult + >(client, get(optionsStore)) + let mutate: CreateMutateFunction optionsStore.subscribe(($options) => { mutate = (variables, mutateOptions) => { diff --git a/packages/svelte-query/src/types.ts b/packages/svelte-query/src/types.ts index ef7ba2e384..7a25a15bce 100644 --- a/packages/svelte-query/src/types.ts +++ b/packages/svelte-query/src/types.ts @@ -87,9 +87,9 @@ export type CreateMutationOptions< TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, + TOnMutateResult = unknown, > = OmitKeyof< - MutationObserverOptions, + MutationObserverOptions, '_defaulted' > @@ -97,28 +97,35 @@ export type CreateMutateFunction< TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, + TOnMutateResult = unknown, > = ( - ...args: Parameters> + ...args: Parameters< + MutateFunction + > ) => void export type CreateMutateAsyncFunction< TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, -> = MutateFunction + TOnMutateResult = unknown, +> = MutateFunction export type CreateBaseMutationResult< TData = unknown, TError = DefaultError, TVariables = unknown, - TContext = unknown, + TOnMutateResult = unknown, > = Override< - MutationObserverResult, - { mutate: CreateMutateFunction } + MutationObserverResult, + { mutate: CreateMutateFunction } > & { - mutateAsync: CreateMutateAsyncFunction + mutateAsync: CreateMutateAsyncFunction< + TData, + TError, + TVariables, + TOnMutateResult + > } /** Result from createMutation */ @@ -126,8 +133,10 @@ export type CreateMutationResult< TData = unknown, TError = DefaultError, TVariables = unknown, - TContext = unknown, -> = Readable> + TOnMutateResult = unknown, +> = Readable< + CreateBaseMutationResult +> /** Options for useMutationState */ export type MutationStateOptions = { diff --git a/packages/vue-query/src/mutationCache.ts b/packages/vue-query/src/mutationCache.ts index f97f6a77ec..026632cff3 100644 --- a/packages/vue-query/src/mutationCache.ts +++ b/packages/vue-query/src/mutationCache.ts @@ -12,10 +12,10 @@ export class MutationCache extends MC { TData = unknown, TError = DefaultError, TVariables = any, - TContext = unknown, + TOnMutateResult = unknown, >( filters: MaybeRefDeep, - ): Mutation | undefined { + ): Mutation | undefined { return super.find(cloneDeepUnref(filters)) } diff --git a/packages/vue-query/src/queryClient.ts b/packages/vue-query/src/queryClient.ts index 2dfc53e803..7f9a0894bf 100644 --- a/packages/vue-query/src/queryClient.ts +++ b/packages/vue-query/src/queryClient.ts @@ -456,11 +456,11 @@ export class QueryClient extends QC { TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, + TOnMutateResult = unknown, >( mutationKey: MaybeRefDeep, options: MaybeRefDeep< - MutationObserverOptions + MutationObserverOptions >, ): void { super.setMutationDefaults( diff --git a/packages/vue-query/src/useMutation.ts b/packages/vue-query/src/useMutation.ts index d507bbc74f..2a2af89aa6 100644 --- a/packages/vue-query/src/useMutation.ts +++ b/packages/vue-query/src/useMutation.ts @@ -24,53 +24,64 @@ import type { import type { MaybeRefDeep, ShallowOption } from './types' import type { QueryClient } from './queryClient' -type MutationResult = DistributiveOmit< - MutationObserverResult, - 'mutate' | 'reset' -> +type MutationResult = + DistributiveOmit< + MutationObserverResult, + 'mutate' | 'reset' + > -type UseMutationOptionsBase = - MutationObserverOptions & ShallowOption +type UseMutationOptionsBase = + MutationObserverOptions & + ShallowOption export type UseMutationOptions< TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, -> = MaybeRefDeep> + TOnMutateResult = unknown, +> = MaybeRefDeep< + UseMutationOptionsBase +> type MutateSyncFunction< TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, + TOnMutateResult = unknown, > = ( - ...options: Parameters> + ...options: Parameters< + MutateFunction + > ) => void export type UseMutationReturnType< TData, TError, TVariables, - TContext, - TResult = MutationResult, + TOnMutateResult, + TResult = MutationResult, > = ToRefs> & { - mutate: MutateSyncFunction - mutateAsync: MutateFunction - reset: MutationObserverResult['reset'] + mutate: MutateSyncFunction + mutateAsync: MutateFunction + reset: MutationObserverResult< + TData, + TError, + TVariables, + TOnMutateResult + >['reset'] } export function useMutation< TData = unknown, TError = DefaultError, TVariables = void, - TContext = unknown, + TOnMutateResult = unknown, >( mutationOptions: MaybeRefDeep< - UseMutationOptionsBase + UseMutationOptionsBase >, queryClient?: QueryClient, -): UseMutationReturnType { +): UseMutationReturnType { if (process.env.NODE_ENV === 'development') { if (!getCurrentScope()) { console.warn( @@ -94,7 +105,7 @@ export function useMutation< const mutate = ( variables: TVariables, - mutateOptions?: MutateOptions, + mutateOptions?: MutateOptions, ) => { observer.mutate(variables, mutateOptions).catch(() => { // This is intentional @@ -114,7 +125,7 @@ export function useMutation< : readonly(state) const resultRefs = toRefs(readonlyState) as ToRefs< - Readonly> + Readonly> > watch(