From ed887bd84405e6618948c4335b68cf7ad0e842df Mon Sep 17 00:00:00 2001 From: Sergey Volkov Date: Thu, 9 Apr 2026 17:46:01 +0300 Subject: [PATCH 1/3] refactor!: remove all deprecations --- .changeset/huge-ties-turn.md | 10 ++++ src/base-query.ts | 6 +- src/infinite-query.test.ts | 22 +++---- src/inifinite-query.ts | 16 ----- src/inifinite-query.types.ts | 84 -------------------------- src/mutation.test.ts | 8 +-- src/mutation.ts | 15 +---- src/mutation.types.ts | 44 -------------- src/preset/create-query.test.ts | 8 +-- src/query-client.ts | 5 -- src/query-client.types.ts | 25 -------- src/query.test.ts | 6 +- src/query.ts | 11 ---- src/query.types.ts | 102 -------------------------------- src/utils/destroyable.ts | 7 --- 15 files changed, 34 insertions(+), 335 deletions(-) create mode 100644 .changeset/huge-ties-turn.md diff --git a/.changeset/huge-ties-turn.md b/.changeset/huge-ties-turn.md new file mode 100644 index 0000000..f322600 --- /dev/null +++ b/.changeset/huge-ties-turn.md @@ -0,0 +1,10 @@ +--- +"mobx-tanstack-query": major +--- + +### Breaking changes + +- Removed deprecated `Destroyable.dispose()` — use `destroy()` or `Symbol.dispose` / `using` where supported. +- Removed `Mobx*` type aliases and `MobxQuery` / `MobxMutation` / `MobxInfiniteQuery` class aliases; import the canonical names (`Query`, `Mutation`, `InfiniteQuery`, `QueryOptions`, `DefaultOptions`, etc.). +- Removed deprecated query client aliases: `IQueryClient`, `QueryClientInterface`, `MobxDefaultOptions`, `MobxQueryClientHooks`, `MobxQueryClientConfig`. +- Removed `resetOnDispose` from query features and mutation options / defaults — use `resetOnDestroy` only. diff --git a/src/base-query.ts b/src/base-query.ts index bb1704b..6b42fad 100644 --- a/src/base-query.ts +++ b/src/base-query.ts @@ -118,11 +118,7 @@ export abstract class BaseQuery< config.cumulativeQueryHash ?? qf?.cumulativeQueryHash, enableOnDemand: config.enableOnDemand ?? qf?.enableOnDemand, lazy: config.lazy ?? qf?.lazy, - resetOnDestroy: - config.resetOnDestroy ?? - config.resetOnDispose ?? - qf?.resetOnDestroy ?? - qf?.resetOnDispose, + resetOnDestroy: config.resetOnDestroy ?? qf?.resetOnDestroy, removeOnDestroy: config.removeOnDestroy ?? qf?.removeOnDestroy, transformError: config.transformError ?? qf?.transformError, dynamicOptionsUpdateDelay: diff --git a/src/infinite-query.test.ts b/src/infinite-query.test.ts index 00c3819..ac5bc60 100644 --- a/src/infinite-query.test.ts +++ b/src/infinite-query.test.ts @@ -30,7 +30,7 @@ class InfiniteQueryMock< queryFn: null as unknown as ReturnType, setData: vi.fn(), update: vi.fn(), - dispose: vi.fn(), + destroy: vi.fn(), refetch: vi.fn(), invalidate: vi.fn(), onDone: vi.fn(), @@ -107,9 +107,9 @@ class InfiniteQueryMock< return result; } - dispose(): void { - const result = super.dispose(); - this.spies.dispose.mockReturnValue(result)(); + destroy(): void { + const result = super.destroy(); + this.spies.destroy.mockReturnValue(result)(); } } @@ -130,7 +130,7 @@ describe('InfiniteQuery', () => { queryKey: ['test'], }); - query.dispose(); + query.destroy(); }); it('should call queryFn with initialPageParam', async () => { @@ -150,7 +150,7 @@ describe('InfiniteQuery', () => { queryKey: ['test'], }); - query.dispose(); + query.destroy(); }); it('should use initialPageParam from dynamic options without top-level initialPageParam', async () => { @@ -295,7 +295,7 @@ describe('InfiniteQuery', () => { queryKey: ['test'], }); - query.dispose(); + query.destroy(); }); it('should call queryFn with getNextPageParam returning null', async () => { @@ -352,7 +352,7 @@ describe('InfiniteQuery', () => { status: 'success', }); - query.dispose(); + query.destroy(); }); it('should call queryFn after fetchNextPage call', async () => { @@ -438,7 +438,7 @@ describe('InfiniteQuery', () => { status: 'success', }); - query.dispose(); + query.destroy(); }); it('should call queryFn after fetchNextPage call (x3 times)', async () => { @@ -536,7 +536,7 @@ describe('InfiniteQuery', () => { status: 'success', }); - query.dispose(); + query.destroy(); }); describe('"enabled" reactive parameter', () => { @@ -556,7 +556,7 @@ describe('InfiniteQuery', () => { expect(query.spies.queryFn).toBeCalledTimes(1); expect(query.spies.queryFn).nthReturnedWith(1, 100); - query.dispose(); + query.destroy(); }); }); diff --git a/src/inifinite-query.ts b/src/inifinite-query.ts index 7b7c0f2..fbcb248 100644 --- a/src/inifinite-query.ts +++ b/src/inifinite-query.ts @@ -381,19 +381,3 @@ export class InfiniteQuery< this.hooks?.onInfiniteQueryDestroy?.(this); } } - -/** - * @deprecated ⚠️ use `InfiniteQuery`. This export will be removed in next major release - */ -export class MobxInfiniteQuery< - TData, - TError = DefaultError, - TQueryKey extends QueryKey = any, - TPageParam = unknown, -> extends InfiniteQuery< - TData, - TError, - TPageParam, - InfiniteData, - TQueryKey -> {} diff --git a/src/inifinite-query.types.ts b/src/inifinite-query.types.ts index 0e1b8eb..4174f29 100644 --- a/src/inifinite-query.types.ts +++ b/src/inifinite-query.types.ts @@ -32,19 +32,9 @@ export type InfiniteQueryDoneListener = ( export interface InfiniteQueryInvalidateParams extends QueryInvalidateParams {} -/** - * @deprecated ⚠️ use `InfiniteQueryInvalidateParams`. This type will be removed in next major release - */ -export type MobxInfiniteQueryInvalidateParams = InfiniteQueryInvalidateParams; - export interface InfiniteQueryResetParams extends QueryResetParams {} export interface InfiniteQueryRemoveParams extends QueryRemoveParams {} -/** - * @deprecated ⚠️ use `InfiniteQueryResetParams`. This type will be removed in next major release - */ -export type MobxInfiniteQueryResetParams = InfiniteQueryResetParams; - type InfiniteQueryOptionTypeFixes< TQueryFnData = unknown, TError = DefaultError, @@ -86,22 +76,6 @@ export interface InfiniteQueryDynamicOptions< enabled?: boolean; } -/** - * @deprecated ⚠️ use `InfiniteQueryDynamicOptions`. This type will be removed in next major release - */ -export type MobxInfiniteQueryDynamicOptions< - TData, - TError = DefaultError, - TQueryKey extends QueryKey = QueryKey, - TPageParam = unknown, -> = InfiniteQueryDynamicOptions< - TData, - TError, - TPageParam, - InfiniteData, - TQueryKey ->; - export interface InfiniteQueryOptions< TQueryFnData = unknown, TError = DefaultError, @@ -126,22 +100,6 @@ export interface InfiniteQueryOptions< TQueryKey > {} -/** - * @deprecated ⚠️ use `InfiniteQueryOptions`. This type will be removed in next major release - */ -export type MobxInfiniteQueryOptions< - TData, - TError = DefaultError, - TQueryKey extends QueryKey = QueryKey, - TPageParam = unknown, -> = InfiniteQueryOptions< - TData, - TError, - TPageParam, - InfiniteData, - TQueryKey ->; - export interface InfiniteQueryUpdateOptions< TQueryFnData = unknown, TError = DefaultError, @@ -183,22 +141,6 @@ export interface InfiniteQueryStartParams< >, Pick {} -/** - * @deprecated ⚠️ use `InfiniteQueryUpdateOptions`. This type will be removed in next major release - */ -export type MobxInfiniteQueryUpdateOptions< - TData, - TError = DefaultError, - TQueryKey extends QueryKey = QueryKey, - TPageParam = unknown, -> = InfiniteQueryUpdateOptions< - TData, - TError, - TPageParam, - InfiniteData, - TQueryKey ->; - export type InfiniteQueryConfigFromFn< TFn extends (...args: any[]) => any, TError = DefaultError, @@ -240,16 +182,6 @@ export type InfiniteQueryUpdateOptionsAllVariants< TQueryKey >; -/** - * @deprecated ⚠️ use `InfiniteQueryConfigFromFn`. This type will be removed in next major release - */ -export type MobxInfiniteQueryConfigFromFn< - TFn extends (...args: any[]) => any, - TError = DefaultError, - TQueryKey extends QueryKey = QueryKey, - TPageParam = unknown, -> = InfiniteQueryConfigFromFn; - export interface InfiniteQueryConfig< TQueryFnData = unknown, TError = DefaultError, @@ -332,22 +264,6 @@ export interface InfiniteQueryFlattenConfig< queryKey?: TQueryKey; } -/** - * @deprecated ⚠️ use `InfiniteQueryConfig`. This type will be removed in next major release - */ -export type MobxInfiniteQueryConfig< - TData, - TError = DefaultError, - TQueryKey extends QueryKey = QueryKey, - TPageParam = unknown, -> = InfiniteQueryConfig< - TData, - TError, - TPageParam, - InfiniteData, - TQueryKey ->; - export type InferInfiniteQuery< T extends | InfiniteQueryConfig diff --git a/src/mutation.test.ts b/src/mutation.test.ts index d173bec..064b060 100644 --- a/src/mutation.test.ts +++ b/src/mutation.test.ts @@ -20,7 +20,7 @@ class MutationMock< > extends Mutation { spies = { mutationFn: null as unknown as ReturnType, - dispose: vi.fn(), + destroy: vi.fn(), reset: vi.fn(), onDone: vi.fn(), onError: vi.fn(), @@ -54,9 +54,9 @@ class MutationMock< this.spies.reset.mockReturnValue(result)(); } - dispose(): void { - const result = super.dispose(); - this.spies.dispose.mockReturnValue(result)(); + destroy(): void { + const result = super.destroy(); + this.spies.destroy.mockReturnValue(result)(); } } diff --git a/src/mutation.ts b/src/mutation.ts index 8e9ec09..79773aa 100644 --- a/src/mutation.ts +++ b/src/mutation.ts @@ -167,10 +167,7 @@ export class Mutation< config.invalidateByKey ?? qc.mutationFeatures?.invalidateByKey, lazy: config.lazy ?? qc.mutationFeatures?.lazy, resetOnDestroy: - config.resetOnDestroy ?? - config.resetOnDispose ?? - qc.mutationFeatures?.resetOnDestroy ?? - qc.mutationFeatures?.resetOnDispose, + config.resetOnDestroy ?? qc.mutationFeatures?.resetOnDestroy, transformError: config.transformError ?? qc.mutationFeatures?.transformError, }; @@ -393,13 +390,3 @@ export class Mutation< this.hooks?.onMutationDestroy?.(this); } } - -/** - * @deprecated ⚠️ use `Mutation`. This export will be removed in next major release - */ -export class MobxMutation< - TData = unknown, - TVariables = void, - TError = DefaultError, - TOnMutateResult = unknown, -> extends Mutation {} diff --git a/src/mutation.types.ts b/src/mutation.types.ts index ff09304..8e23e45 100644 --- a/src/mutation.types.ts +++ b/src/mutation.types.ts @@ -18,12 +18,6 @@ export interface MutationFeatures { invalidateByKey?: | boolean | Omit; - /** - * Reset mutation when dispose is called - * - * @deprecated Please use 'resetOnDestroy' - */ - resetOnDispose?: boolean; /** * Reset mutation when destroy or abort signal is called @@ -39,11 +33,6 @@ export interface MutationFeatures { transformError?: (error: any) => any; } -/** - * @deprecated ⚠️ use `MutationFeatures`. This type will be removed in next major release - */ -export type MobxMutationFeatures = MutationFeatures; - export interface MutationInvalidateQueriesOptions extends Omit { queryKey?: InvalidateQueryFilters['queryKey']; @@ -51,25 +40,11 @@ export interface MutationInvalidateQueriesOptions allQueryKeys?: true; } -/** - * @deprecated ⚠️ use `MutationInvalidateQueriesOptions`. This type will be removed in next major release - */ -export type MobxMutationInvalidateQueriesOptions = - MutationInvalidateQueriesOptions; - export type MutationFn = ( variables: TVariables, context: MutationFunctionContext, ) => Promise; -/** - * @deprecated ⚠️ use `MutationFn`. This type will be removed in next major release - */ -export type MobxMutationFunction< - TData = unknown, - TVariables = unknown, -> = MutationFn; - export type MutationSettledListener< TData = unknown, TError = DefaultError, @@ -119,16 +94,6 @@ export interface MutationFunctionContext extends MutationFunctionContextCore { signal: AbortSignal; } -/** - * @deprecated ⚠️ use `MutationConfig`. This type will be removed in next major release - */ -export type MobxMutationConfig< - TData = unknown, - TVariables = void, - TError = DefaultError, - TContext = unknown, -> = MutationConfig; - export type MutationConfigFromFn< T extends (...args: any[]) => any, TError = DefaultError, @@ -140,15 +105,6 @@ export type MutationConfigFromFn< TContext >; -/** - * @deprecated ⚠️ use `MutationConfigFromFn`. This type will be removed in next major release - */ -export type MobxMutationConfigFromFn< - T extends (...args: any[]) => any, - TError = DefaultError, - TContext = unknown, -> = MutationConfigFromFn; - export type InferMutation< T extends MutationConfig | Mutation, TInferValue extends diff --git a/src/preset/create-query.test.ts b/src/preset/create-query.test.ts index 5c5ec21..eb6289b 100644 --- a/src/preset/create-query.test.ts +++ b/src/preset/create-query.test.ts @@ -105,7 +105,7 @@ describe('createQuery', () => { expect(query.options.queryKey).toEqual(['plain-options']); expect(query.options.queryFn).toBe(queryFn); - query.dispose(); + query.destroy(); }); it('creates query from queryFn overload and keeps initialData', () => { @@ -122,7 +122,7 @@ describe('createQuery', () => { expect(query.options.queryFn).toBe(queryFn); expect(query.result.data).toBe(2); - query.dispose(); + query.destroy(); }); it('rejects mismatched initialData for queryFn overload', () => { @@ -148,7 +148,7 @@ describe('createQuery', () => { expect(query.result.data).toBe('2'); - query.dispose(); + query.destroy(); }); it('creates query from queryClient and dynamic options overload', () => { @@ -165,6 +165,6 @@ describe('createQuery', () => { expect(query.options.queryKey).toEqual(['dynamic-options']); expect(query.options.queryFn).toBe(queryFn); - query.dispose(); + query.destroy(); }); }); diff --git a/src/query-client.ts b/src/query-client.ts index e7bbc43..3ed9fd0 100644 --- a/src/query-client.ts +++ b/src/query-client.ts @@ -35,8 +35,3 @@ export class QueryClient extends QueryClientCore implements IQueryClientCore { return this.getDefaultOptions().mutations ?? {}; } } - -/** - * @deprecated ⚠️ use `QueryClient`. This export will be removed in next major release - */ -export class MobxQueryClient extends QueryClient {} diff --git a/src/query-client.types.ts b/src/query-client.types.ts index a83b6d0..f44f143 100644 --- a/src/query-client.types.ts +++ b/src/query-client.types.ts @@ -15,16 +15,6 @@ export type IQueryClientCore = { [K in keyof QueryClientCore]: QueryClientCore[K]; }; -/** - * @deprecated ⚠️ use `IQueryClientCore`. This type will be removed in next major release - */ -export type IQueryClient = IQueryClientCore; - -/** - * @deprecated renamed to `IQueryClient`. Will be removed in next major release. - */ -export type QueryClientInterface = IQueryClientCore; - export type AnyQueryClient = QueryClient | IQueryClientCore | QueryClientCore; export interface DefaultOptions @@ -33,11 +23,6 @@ export interface DefaultOptions mutations?: DefaultCoreOptions['mutations'] & MutationFeatures; } -/** - * @deprecated ⚠️ use `DefaultOptions`. This type will be removed in next major release - */ -export type MobxDefaultOptions = DefaultOptions; - export interface QueryClientHooks { onQueryInit?: (query: AnyQuery) => void; onInfiniteQueryInit?: (query: InfiniteQuery) => void; @@ -49,18 +34,8 @@ export interface QueryClientHooks { onMutationDestroy?: (query: Mutation) => void; } -/** - * @deprecated ⚠️ use `QueryClientHooks`. This type will be removed in next major release - */ -export type MobxQueryClientHooks = QueryClientHooks; - export interface QueryClientConfig extends Omit { defaultOptions?: DefaultOptions; hooks?: QueryClientHooks; } - -/** - * @deprecated ⚠️ use `QueryClientConfig`. This type will be removed in next major release - */ -export type MobxQueryClientConfig = QueryClientConfig; diff --git a/src/query.test.ts b/src/query.test.ts index bafd5ed..cc6f02a 100644 --- a/src/query.test.ts +++ b/src/query.test.ts @@ -54,7 +54,7 @@ class QueryMock< queryFn: null as unknown as ReturnType, setData: vi.fn(), update: vi.fn(), - dispose: vi.fn(), + destroy: vi.fn(), refetch: vi.fn(), invalidate: vi.fn(), onDone: vi.fn(), @@ -121,9 +121,9 @@ class QueryMock< return result; } - dispose(): void { + destroy(): void { const result = super.destroy(); - this.spies.dispose.mockReturnValue(result)(); + this.spies.destroy.mockReturnValue(result)(); } } diff --git a/src/query.ts b/src/query.ts index 97ef32c..7bc144a 100644 --- a/src/query.ts +++ b/src/query.ts @@ -359,14 +359,3 @@ export class Query< this.hooks?.onQueryDestroy?.(this); } } - -/** - * @deprecated ⚠️ use `Query`. This export will be removed in next major release - */ -export class MobxQuery< - TQueryFnData = unknown, - TError = DefaultError, - TData = TQueryFnData, - TQueryData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, -> extends Query {} diff --git a/src/query.types.ts b/src/query.types.ts index cdc3947..8340ac9 100644 --- a/src/query.types.ts +++ b/src/query.types.ts @@ -13,11 +13,6 @@ import type { AnyQueryClient } from './query-client.types.js'; export interface QueryInvalidateParams extends Partial> {} -/** - * @deprecated ⚠️ use `QueryInvalidateParams`. This type will be removed in next major release - */ -export type MobxQueryInvalidateParams = QueryInvalidateParams; - export interface QueryResetParams extends Partial> {} @@ -29,11 +24,6 @@ export interface QueryRemoveParams safe?: boolean; } -/** - * @deprecated ⚠️ use `QueryResetParams`. This type will be removed in next major release - */ -export type MobxQueryResetParams = QueryResetParams; - export interface QueryDynamicOptions< TQueryFnData = unknown, TError = DefaultError, @@ -49,17 +39,6 @@ export interface QueryDynamicOptions< enabled?: boolean; } -/** - * @deprecated ⚠️ use `QueryDynamicOptions`. This type will be removed in next major released - */ -export type MobxQueryDynamicOptions< - TQueryFnData = unknown, - TError = DefaultError, - TData = TQueryFnData, - TQueryData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, -> = QueryDynamicOptions; - export interface QueryOptions< TQueryFnData = unknown, TError = DefaultError, @@ -74,17 +53,6 @@ export interface QueryOptions< TQueryKey > {} -/** - * @deprecated ⚠️ use `QueryOptions`. This type will be removed in next major release - */ -export type MobxQueryOptions< - TQueryFnData = unknown, - TError = DefaultError, - TData = TQueryFnData, - TQueryData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, -> = QueryOptions; - export type QueryUpdateOptions< TQueryFnData = unknown, TError = DefaultError, @@ -95,25 +63,7 @@ export type QueryUpdateOptions< QueryObserverOptions >; -/** - * @deprecated ⚠️ use `QueryUpdateOptions`. This type will be removed in next major release - */ -export type MobxQueryUpdateOptions< - TQueryFnData = unknown, - TError = DefaultError, - TData = TQueryFnData, - TQueryData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, -> = QueryUpdateOptions; - export interface QueryFeatures { - /** - * Reset query when dispose is called - * - * @deprecated Please use 'resetOnDestroy'. This type will be removed in next major release - */ - resetOnDispose?: boolean; - /** * Reset query when destroy method called or abort signal is called * @@ -182,11 +132,6 @@ export interface QueryFeatures { dynamicOptionsComparer?: IReactionOptions['equals']; } -/** - * @deprecated ⚠️ use `QueryFeatures`. This type will be removed in next major release - */ -export type MobxQueryFeatures = QueryFeatures; - export type QueryConfigFromFn< TFunction extends (...args: any[]) => any, TError = DefaultError, @@ -199,15 +144,6 @@ export type QueryConfigFromFn< TQueryKey >; -/** - * @deprecated ⚠️ use `QueryConfigFromFn`. This type will be removed in next major release - */ -export type MobxQueryConfigFromFn< - TFunction extends (...args: any[]) => any, - TError = DefaultError, - TQueryKey extends QueryKey = QueryKey, -> = QueryConfigFromFn; - export type QueryErrorListener = ( error: TError, payload: void, @@ -272,17 +208,6 @@ export interface QueryConfig< ) => QueryDynamicOptions; } -/** - * @deprecated ⚠️ use `QueryConfig`. This type will be removed in next major release - */ -export type MobxQueryConfig< - TQueryFnData = unknown, - TError = DefaultError, - TData = TQueryFnData, - TQueryData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, -> = QueryConfig; - export type QueryFn< TQueryFnData = unknown, TError = DefaultError, @@ -305,24 +230,8 @@ export type QueryUpdateOptionsAllVariants< | QueryUpdateOptions | QueryDynamicOptions; -/** - * @deprecated ⚠️ use `QueryFn`. This type will be removed in next major release - */ -export type MobxQueryFn< - TQueryFnData = unknown, - TError = DefaultError, - TData = TQueryFnData, - TQueryData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, -> = QueryFn; - export type AnyQuery = Query; -/** - * @deprecated ⚠️ use `AnyQuery`. This type will be removed in next major release - */ -export type AnyMobxQuery = AnyQuery; - export interface QueryStartParams< TQueryFnData = unknown, TError = DefaultError, @@ -337,17 +246,6 @@ export interface QueryStartParams< TQueryKey > {} -/** - * @deprecated ⚠️ use `QueryStartParams`. This type will be removed in next major release - */ -export type MobxQueryStartParams< - TQueryFnData = unknown, - TError = DefaultError, - TData = TQueryFnData, - TQueryData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, -> = QueryStartParams; - export type InferQuery< T extends QueryConfig | Query, TInferValue extends 'data' | 'key' | 'error' | 'query' | 'config', diff --git a/src/utils/destroyable.ts b/src/utils/destroyable.ts index 09ef179..ec0451e 100644 --- a/src/utils/destroyable.ts +++ b/src/utils/destroyable.ts @@ -21,13 +21,6 @@ export abstract class Destroyable implements Disposable { protected abstract handleDestroy(): void; - /** - * @deprecated use `destroy`. This method will be removed in next major release - */ - dispose() { - this.destroy(); - } - [Symbol.dispose](): void { this.destroy(); } From 1d429b9dcf553bac9093c7af20092c93ba3b3c65 Mon Sep 17 00:00:00 2001 From: Sergey Volkov Date: Fri, 10 Apr 2026 00:27:10 +0300 Subject: [PATCH 2/3] fix: apply comments at PR --- src/mutation.test.ts | 91 ++++++++++++++++++++++++++++++++++++++ src/mutation.ts | 1 + src/query.test.ts | 103 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 195 insertions(+) diff --git a/src/mutation.test.ts b/src/mutation.test.ts index 064b060..59411dc 100644 --- a/src/mutation.test.ts +++ b/src/mutation.test.ts @@ -11,6 +11,7 @@ import type { MutationFn, MutationFunctionContext, } from './mutation.types.js'; +import { QueryClient as MobxQueryClient } from './query-client.js'; class MutationMock< TData = unknown, @@ -219,4 +220,94 @@ describe('Mutation', () => { Mutation >(); }); + + describe('resetOnDestroy vs deprecated resetOnDispose (Mutation constructor features)', () => { + /** + * Targets `resetOnDestroy: config.resetOnDestroy ?? qc.mutationFeatures?.resetOnDestroy`. + * `qc.mutationFeatures.resetOnDispose` must not enable reset-on-destroy. + */ + it('does not use qc.mutationFeatures.resetOnDispose when config.resetOnDestroy is undefined', async () => { + const queryClient = new MobxQueryClient({ + defaultOptions: { + // Legacy runtime key; must not map to reset-on-destroy (see Mutation constructor). + mutations: { + resetOnDispose: true, + } as MobxQueryClient['mutationFeatures'], + }, + }); + + const mutation = new Mutation({ + queryClient, + mutationKey: ['reset-regression'], + mutationFn: async () => 'ok', + }); + + await mutation.mutate(); + expect(mutation.result.status).toBe('success'); + mutation.destroy(); + expect(mutation.result.status).toBe('success'); + }); + + it('honors qc.mutationFeatures.resetOnDestroy when config.resetOnDestroy is undefined', async () => { + const queryClient = new MobxQueryClient({ + defaultOptions: { + mutations: { resetOnDestroy: true }, + }, + }); + + const mutation = new Mutation({ + queryClient, + mutationKey: ['reset-regression-qc'], + mutationFn: async () => 'ok', + }); + + await mutation.mutate(); + expect(mutation.result.status).toBe('success'); + mutation.destroy(); + expect(mutation.result.status).toBe('idle'); + }); + + it('config.resetOnDestroy true wins over qc.mutationFeatures.resetOnDispose', async () => { + const queryClient = new MobxQueryClient({ + defaultOptions: { + mutations: { + resetOnDestroy: false, + resetOnDispose: true, + } as MobxQueryClient['mutationFeatures'], + }, + }); + + const mutation = new Mutation({ + queryClient, + mutationKey: ['reset-regression-both'], + mutationFn: async () => 'ok', + resetOnDestroy: true, + }); + + await mutation.mutate(); + expect(mutation.result.status).toBe('success'); + mutation.destroy(); + expect(mutation.result.status).toBe('idle'); + }); + + it('explicit config.resetOnDestroy false overrides qc.mutationFeatures.resetOnDestroy', async () => { + const queryClient = new MobxQueryClient({ + defaultOptions: { + mutations: { resetOnDestroy: true }, + }, + }); + + const mutation = new Mutation({ + queryClient, + mutationKey: ['reset-regression-config-false'], + mutationFn: async () => 'ok', + resetOnDestroy: false, + }); + + await mutation.mutate(); + expect(mutation.result.status).toBe('success'); + mutation.destroy(); + expect(mutation.result.status).toBe('success'); + }); + }); }); diff --git a/src/mutation.ts b/src/mutation.ts index 79773aa..af42fda 100644 --- a/src/mutation.ts +++ b/src/mutation.ts @@ -384,6 +384,7 @@ export class Mutation< if (this.features.resetOnDestroy) { this.reset(); + this.updateResult(this.mutationObserver.getCurrentResult()); } delete this._observerSubscription; diff --git a/src/query.test.ts b/src/query.test.ts index cc6f02a..fdd9e40 100644 --- a/src/query.test.ts +++ b/src/query.test.ts @@ -4316,6 +4316,109 @@ describe('Query', () => { } }); + describe('resetOnDestroy vs deprecated resetOnDispose (BaseQuery.mergeQueryFeatures)', () => { + /** + * Uses the same construction path as production: `new Query` → `BaseQuery.mergeQueryFeatures(config, queryClient)`. + * `resetOnDispose` is no longer read; only `config.resetOnDestroy ?? queryClient.queryFeatures?.resetOnDestroy`. + */ + const makeRegressionQuery = ( + queryClient: MobxQueryClient, + queryFn: () => Promise, + featurePatch: Record, + ) => + new Query({ + queryClient, + staleTime: Infinity, + queryKey: ['merge-features-reset-regression'], + queryFn, + ...featurePatch, + } as QueryConfig); + + it('honors config.resetOnDestroy: second instance refetches after destroy (cache reset)', async () => { + let counter = 0; + const queryFn = vi.fn(async () => ++counter); + const queryClient = new MobxQueryClient({}); + + const first = makeRegressionQuery(queryClient, queryFn, { + resetOnDestroy: true, + }); + await when(() => first.result.data === 1); + first.destroy(); + + const second = makeRegressionQuery(queryClient, queryFn, { + resetOnDestroy: true, + }); + await when(() => second.result.data === 2); + expect(queryFn).toHaveBeenCalledTimes(2); + + second.destroy(); + }); + + it('does not fall back to resetOnDispose alone: cache kept, second instance reuses data', async () => { + let counter = 0; + const queryFn = vi.fn(async () => ++counter); + const queryClient = new MobxQueryClient({}); + + const first = makeRegressionQuery(queryClient, queryFn, { + resetOnDispose: true, + }); + await when(() => first.result.data === 1); + first.destroy(); + + const second = makeRegressionQuery(queryClient, queryFn, { + resetOnDispose: true, + }); + await when(() => second.result.data === 1); + expect(queryFn).toHaveBeenCalledTimes(1); + + second.destroy(); + }); + + it('when both keys exist, explicit resetOnDestroy: false wins over resetOnDispose (no cache reset)', async () => { + let counter = 0; + const queryFn = vi.fn(async () => ++counter); + const queryClient = new MobxQueryClient({}); + + const first = makeRegressionQuery(queryClient, queryFn, { + resetOnDestroy: false, + resetOnDispose: true, + }); + await when(() => first.result.data === 1); + first.destroy(); + + const second = makeRegressionQuery(queryClient, queryFn, { + resetOnDestroy: false, + resetOnDispose: true, + }); + await when(() => second.result.data === 1); + expect(queryFn).toHaveBeenCalledTimes(1); + + second.destroy(); + }); + + it('when both keys exist, resetOnDestroy: true is honored (resetOnDispose does not block reset)', async () => { + let counter = 0; + const queryFn = vi.fn(async () => ++counter); + const queryClient = new MobxQueryClient({}); + + const first = makeRegressionQuery(queryClient, queryFn, { + resetOnDestroy: true, + resetOnDispose: false, + }); + await when(() => first.result.data === 1); + first.destroy(); + + const second = makeRegressionQuery(queryClient, queryFn, { + resetOnDestroy: true, + resetOnDispose: false, + }); + await when(() => second.result.data === 2); + expect(queryFn).toHaveBeenCalledTimes(2); + + second.destroy(); + }); + }); + it('onDone should work after destroy with removeOnDestroy from query client', async () => { vi.useRealTimers(); let counter = 0; From 9d6de94e259f4fe2f4ffd4255592019578c49e47 Mon Sep 17 00:00:00 2001 From: Sergey Volkov Date: Fri, 10 Apr 2026 00:38:03 +0300 Subject: [PATCH 3/3] fix: apply comments at PR --- src/query.test.ts | 68 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/src/query.test.ts b/src/query.test.ts index fdd9e40..e7d3ee9 100644 --- a/src/query.test.ts +++ b/src/query.test.ts @@ -4417,6 +4417,74 @@ describe('Query', () => { second.destroy(); }); + + describe('queryClient.queryFeatures fallback (config omits reset flags)', () => { + /** + * `Query` ctor calls `BaseQuery.mergeQueryFeatures(config, queryClient)`; when `config.resetOnDestroy` + * is omitted, `resetOnDestroy` comes from `queryClient.queryFeatures` (`MobxQueryClient`: `getDefaultOptions().queries`). + */ + it('uses queryFeatures.resetOnDestroy true: cache reset on destroy (second Query refetches)', async () => { + let counter = 0; + const queryFn = vi.fn(async () => ++counter); + const queryClient = new MobxQueryClient({ + defaultOptions: { + queries: { resetOnDestroy: true }, + }, + }); + + const first = makeRegressionQuery(queryClient, queryFn, {}); + await when(() => first.result.data === 1); + first.destroy(); + + const second = makeRegressionQuery(queryClient, queryFn, {}); + await when(() => second.result.data === 2); + expect(queryFn).toHaveBeenCalledTimes(2); + + second.destroy(); + }); + + it('uses queryFeatures.resetOnDestroy false: no cache reset (second Query reuses cache)', async () => { + let counter = 0; + const queryFn = vi.fn(async () => ++counter); + const queryClient = new MobxQueryClient({ + defaultOptions: { + queries: { resetOnDestroy: false }, + }, + }); + + const first = makeRegressionQuery(queryClient, queryFn, {}); + await when(() => first.result.data === 1); + first.destroy(); + + const second = makeRegressionQuery(queryClient, queryFn, {}); + await when(() => second.result.data === 1); + expect(queryFn).toHaveBeenCalledTimes(1); + + second.destroy(); + }); + + it('ignores client-level resetOnDispose: mergeQueryFeatures does not read qf.resetOnDispose', async () => { + let counter = 0; + const queryFn = vi.fn(async () => ++counter); + const queryClient = new MobxQueryClient({ + defaultOptions: { + queries: { + resetOnDispose: true, + } as MobxQueryClient['queryFeatures'], + }, + }); + + const first = makeRegressionQuery(queryClient, queryFn, {}); + await when(() => first.result.data === 1); + first.destroy(); + + const second = makeRegressionQuery(queryClient, queryFn, {}); + await when(() => second.result.data === 1); + expect(queryFn).toHaveBeenCalledTimes(1); + + second.destroy(); + }); + }); }); it('onDone should work after destroy with removeOnDestroy from query client', async () => {