diff --git a/package.json b/package.json index dbfccfe3a5..e7400db44b 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "watch": "concurrently --kill-others \"rollup --config rollup.config.js -w\" \"pnpm run typecheck --watch\"", "dev": "pnpm run watch", "prettier": "prettier \"packages/*/{src/**,examples/**/src/**}.{md,js,jsx,ts,tsx,json}\"", - "prettier:write": "pnpm run prettier -- --write", + "prettier:write": "pnpm run prettier --write", "cipublish": "ts-node scripts/publish.ts" }, "namespace": "@tanstack", diff --git a/packages/vue-query/src/__tests__/useMutation.test.ts b/packages/vue-query/src/__tests__/useMutation.test.ts index f28c74e189..3ca4578f09 100644 --- a/packages/vue-query/src/__tests__/useMutation.test.ts +++ b/packages/vue-query/src/__tests__/useMutation.test.ts @@ -1,4 +1,4 @@ -import { reactive } from 'vue-demi' +import { reactive, ref } from 'vue-demi' import { errorMutator, flushPromises, successMutator } from './test-utils' import { parseMutationArgs, useMutation } from '../useMutation' import { useQueryClient } from '../useQueryClient' @@ -35,11 +35,8 @@ describe('useMutation', () => { test('should return error when request fails', async () => { const mutation = useMutation(errorMutator) - - mutation.mutate() - + mutation.mutate({}) await flushPromises(10) - expect(mutation).toMatchObject({ isIdle: { value: false }, isLoading: { value: false }, @@ -88,6 +85,48 @@ describe('useMutation', () => { expect(mutations?.options.mutationKey).toEqual(['bar']) }) + test('should update reactive options deeply', async () => { + type MutationKeyTest = { + entity: string + otherObject: { + name: string + someFn: Function + } + } + const mutationKey = ref([ + { + entity: 'test', + otherObject: { name: 'objectName', someFn: () => null }, + }, + ]) + const queryClient = useQueryClient() + const mutationCache = queryClient.getMutationCache() + const options = reactive({ mutationKey }) + const mutation = useMutation( + (params: string) => successMutator(params), + options, + ) + + mutationKey.value[0]!.otherObject.name = 'someOtherObjectName' + await flushPromises() + mutation.mutate('xyz') + + await flushPromises() + + const mutations = mutationCache.getAll() + const relevantMutation = mutations.find((m) => { + return ( + Array.isArray(m.options.mutationKey) && + !!m.options.mutationKey[0].otherObject + ) + }) + + expect( + (relevantMutation?.options.mutationKey as MutationKeyTest[])[0] + ?.otherObject.name === 'someOtherObjectName', + ) + }) + test('should reset state after invoking mutation.reset', async () => { const mutation = useMutation((params: string) => errorMutator(params)) @@ -237,7 +276,7 @@ describe('useMutation', () => { test('should throw on error', async () => { const mutation = useMutation(errorMutator) - await expect(mutation.mutateAsync()).rejects.toThrowError('Some error') + await expect(mutation.mutateAsync({})).rejects.toThrowError('Some error') expect(mutation).toMatchObject({ isIdle: { value: false }, diff --git a/packages/vue-query/src/types.ts b/packages/vue-query/src/types.ts index a9c8a6f423..f4e6c827dc 100644 --- a/packages/vue-query/src/types.ts +++ b/packages/vue-query/src/types.ts @@ -9,15 +9,14 @@ import type { Ref, UnwrapRef } from 'vue-demi' import type { QueryClient } from './queryClient' export type MaybeRef = Ref | T -export type MaybeRefDeep = T extends Function - ? T - : MaybeRef< - T extends object - ? { - [Property in keyof T]: MaybeRefDeep - } - : T - > + +export type MaybeRefDeep = MaybeRef< + T extends object + ? { + [Property in keyof T]: MaybeRefDeep + } + : T +> export type WithQueryClientKey = T & { queryClientKey?: string diff --git a/packages/vue-query/src/useMutation.ts b/packages/vue-query/src/useMutation.ts index 07fd73ffda..e53715c025 100644 --- a/packages/vue-query/src/useMutation.ts +++ b/packages/vue-query/src/useMutation.ts @@ -1,17 +1,25 @@ -import { onScopeDispose, reactive, readonly, toRefs, watch } from 'vue-demi' +import { + onScopeDispose, + reactive, + readonly, + toRefs, + watch, + computed, + isRef, +} from 'vue-demi' import type { ToRefs } from 'vue-demi' -import { MutationObserver } from '@tanstack/query-core' import type { MutateOptions, MutationFunction, MutationKey, - MutationObserverOptions, MutateFunction, MutationObserverResult, + MutationObserverOptions, } from '@tanstack/query-core' -import { cloneDeepUnref, isQueryKey, updateState } from './utils' +import type { WithQueryClientKey, MaybeRef, MaybeRefDeep } from './types' +import { MutationObserver } from '@tanstack/query-core' +import { cloneDeepUnref, updateState, isMutationKey } from './utils' import { useQueryClient } from './useQueryClient' -import type { WithQueryClientKey } from './types' type MutationResult = Omit< MutationObserverResult, @@ -23,6 +31,22 @@ export type UseMutationOptions = MutationObserverOptions > +export type VueMutationObserverOptions< + TData = unknown, + TError = unknown, + TVariables = void, + TContext = unknown, +> = { + [Property in keyof UseMutationOptions< + TData, + TError, + TVariables, + TContext + >]: MaybeRefDeep< + UseMutationOptions[Property] + > +} + type MutateSyncFunction< TData = unknown, TError = unknown, @@ -50,7 +74,9 @@ export function useMutation< TVariables = void, TContext = unknown, >( - options: UseMutationOptions, + options: MaybeRef< + VueMutationObserverOptions + >, ): UseMutationReturnType export function useMutation< TData = unknown, @@ -58,10 +84,12 @@ export function useMutation< TVariables = void, TContext = unknown, >( - mutationFn: MutationFunction, - options?: Omit< - UseMutationOptions, - 'mutationFn' + mutationFn: MaybeRef>, + options?: MaybeRef< + Omit< + VueMutationObserverOptions, + 'mutationFn' + > >, ): UseMutationReturnType export function useMutation< @@ -70,10 +98,12 @@ export function useMutation< TVariables = void, TContext = unknown, >( - mutationKey: MutationKey, - options?: Omit< - UseMutationOptions, - 'mutationKey' + mutationKey: MaybeRef, + options?: MaybeRef< + Omit< + VueMutationObserverOptions, + 'mutationKey' + > >, ): UseMutationReturnType export function useMutation< @@ -82,11 +112,13 @@ export function useMutation< TVariables = void, TContext = unknown, >( - mutationKey: MutationKey, - mutationFn?: MutationFunction, - options?: Omit< - UseMutationOptions, - 'mutationKey' | 'mutationFn' + mutationKey: MaybeRef, + mutationFn?: MaybeRef>, + options?: MaybeRef< + Omit< + VueMutationObserverOptions, + 'mutationKey' | 'mutationFn' + > >, ): UseMutationReturnType export function useMutation< @@ -96,20 +128,25 @@ export function useMutation< TContext = unknown, >( arg1: - | MutationKey - | MutationFunction - | UseMutationOptions, + | MaybeRef + | MaybeRef> + | MaybeRef>, arg2?: - | MutationFunction - | UseMutationOptions, - arg3?: UseMutationOptions, + | MaybeRef> + | MaybeRef>, + arg3?: MaybeRef< + VueMutationObserverOptions + >, ): UseMutationReturnType { - const options = parseMutationArgs(arg1, arg2, arg3) + const options = computed(() => { + return parseMutationArgs(arg1, arg2, arg3) + }) const queryClient = - options.queryClient ?? useQueryClient(options.queryClientKey) - const defaultedOptions = queryClient.defaultMutationOptions(options) - const observer = new MutationObserver(queryClient, defaultedOptions) - + options.value.queryClient ?? useQueryClient(options.value.queryClientKey) + const observer = new MutationObserver( + queryClient, + queryClient.defaultMutationOptions(options.value), + ) const state = reactive(observer.getCurrentResult()) const unsubscribe = observer.subscribe((result) => { @@ -126,11 +163,9 @@ export function useMutation< } watch( - [() => arg1, () => arg2, () => arg3], + options, () => { - observer.setOptions( - queryClient.defaultMutationOptions(parseMutationArgs(arg1, arg2, arg3)), - ) + observer.setOptions(queryClient.defaultMutationOptions(options.value)) }, { deep: true }, ) @@ -158,26 +193,33 @@ export function parseMutationArgs< TContext = unknown, >( arg1: - | MutationKey - | MutationFunction - | UseMutationOptions, + | MaybeRef + | MaybeRef> + | MaybeRef>, arg2?: - | MutationFunction - | UseMutationOptions, - arg3?: UseMutationOptions, -): UseMutationOptions { + | MaybeRef> + | MaybeRef>, + arg3?: MaybeRef< + VueMutationObserverOptions + >, +): WithQueryClientKey< + MutationObserverOptions +> { let options = arg1 - - if (isQueryKey(arg1)) { - if (typeof arg2 === 'function') { - options = { ...arg3, mutationKey: arg1, mutationFn: arg2 } + if (isMutationKey(arg1)) { + const plainFn = isRef(arg2) ? arg2.value : arg2 + const plainOptions = isRef(arg3) ? arg3.value : arg3 + if (typeof plainFn === 'function') { + options = { ...plainOptions, mutationKey: arg1, mutationFn: plainFn } } else { options = { ...arg2, mutationKey: arg1 } } } - if (typeof arg1 === 'function') { - options = { ...arg2, mutationFn: arg1 } + const plainFn = isRef(arg1) ? arg1.value : arg1 + const plainOptions = isRef(arg2) ? arg2.value : arg2 + if (typeof plainFn === 'function') { + options = { ...plainOptions, mutationFn: plainFn } } return cloneDeepUnref(options) as UseMutationOptions< diff --git a/packages/vue-query/src/utils.ts b/packages/vue-query/src/utils.ts index 7c4e1ab796..2d719c9039 100644 --- a/packages/vue-query/src/utils.ts +++ b/packages/vue-query/src/utils.ts @@ -1,7 +1,8 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import type { QueryKey } from '@tanstack/query-core' +import type { QueryKey, MutationKey } from '@tanstack/query-core' import { isRef, unref } from 'vue-demi' import type { UnwrapRef } from 'vue-demi' +import type { MaybeRef } from './types' export const VUE_QUERY_CLIENT = 'VUE_QUERY_CLIENT' @@ -14,6 +15,10 @@ export function isQueryKey(value: unknown): value is QueryKey { return Array.isArray(value) } +export function isMutationKey(value: unknown): value is MaybeRef { + return Array.isArray(isRef(value) ? value.value : value) +} + export function updateState( state: Record, update: Record,