Skip to content

Commit 24a8a41

Browse files
committed
feat: safe options for queries (remove)
1 parent b626ab4 commit 24a8a41

File tree

7 files changed

+129
-59
lines changed

7 files changed

+129
-59
lines changed

.changeset/lazy-coats-flash.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"mobx-tanstack-query": minor
3+
---
4+
5+
added `autoRemovePreviousQuery` query feature

.changeset/loose-nights-own.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"mobx-tanstack-query": minor
3+
---
4+
5+
added value `"safe"` for `removeOnDestroy` option for query

.changeset/spotty-memes-stay.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"mobx-tanstack-query": minor
3+
---
4+
5+
`safe` option for `remove()` method for queries

src/inifinite-query.ts

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import {
3737
InfiniteQueryUpdateOptionsAllVariants,
3838
} from './inifinite-query.types';
3939
import { Query } from './query';
40+
import { QueryClient } from './query-client';
4041
import { AnyQueryClient, QueryClientHooks } from './query-client.types';
4142
import { QueryFeatures } from './query.types';
4243

@@ -329,27 +330,33 @@ export class InfiniteQuery<
329330
this.errorListeners = [];
330331
this.doneListeners = [];
331332

333+
// simple type override to make typescript happy
334+
// and do less for javascript
335+
const qc = queryClient as unknown as Partial<
336+
Pick<QueryClient, 'queryFeatures' | 'hooks'>
337+
>;
338+
332339
this.features = {
333-
cumulativeQueryHash: config.cumulativeQueryHash,
334-
enableOnDemand: config.enableOnDemand,
335-
lazy: config.lazy,
336-
resetOnDestroy: config.resetOnDestroy,
337-
removeOnDestroy: config.removeOnDestroy,
338-
transformError: config.transformError,
339-
dynamicOptionsUpdateDelay: config.dynamicOptionsUpdateDelay,
340+
cumulativeQueryHash:
341+
config.cumulativeQueryHash ?? qc.queryFeatures?.cumulativeQueryHash,
342+
enableOnDemand: config.enableOnDemand ?? qc.queryFeatures?.enableOnDemand,
343+
lazy: config.lazy ?? qc.queryFeatures?.lazy,
344+
resetOnDestroy:
345+
config.resetOnDestroy ??
346+
config.resetOnDispose ??
347+
qc.queryFeatures?.resetOnDestroy ??
348+
qc.queryFeatures?.resetOnDispose,
349+
removeOnDestroy:
350+
config.removeOnDestroy ?? qc.queryFeatures?.removeOnDestroy,
351+
transformError: config.transformError ?? qc.queryFeatures?.transformError,
352+
dynamicOptionsUpdateDelay:
353+
config.dynamicOptionsUpdateDelay ??
354+
qc.queryFeatures?.dynamicOptionsUpdateDelay,
355+
autoRemovePreviousQuery:
356+
config.autoRemovePreviousQuery ??
357+
qc.queryFeatures?.autoRemovePreviousQuery,
340358
};
341-
342-
if ('queryFeatures' in queryClient) {
343-
this.features.lazy ??= queryClient.queryFeatures.lazy;
344-
this.features.enableOnDemand ??=
345-
queryClient.queryFeatures.enableOnDemand ??
346-
queryClient.queryFeatures.resetOnDispose;
347-
this.features.cumulativeQueryHash ??=
348-
queryClient.queryFeatures.cumulativeQueryHash;
349-
this.features.transformError ??= queryClient.queryFeatures.transformError;
350-
351-
this.hooks = queryClient.hooks;
352-
}
359+
this.hooks = qc.hooks;
353360

354361
observable.deep(this, '_result');
355362
observable.ref(this, 'isResultRequsted');
@@ -632,7 +639,9 @@ export class InfiniteQuery<
632639
}
633640

634641
if (this.features.removeOnDestroy) {
635-
this.remove();
642+
this.remove({
643+
safe: this.features.removeOnDestroy === 'safe',
644+
});
636645
}
637646

638647
delete this._observerSubscription;

src/mutation.ts

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
MutationInvalidateQueriesOptions,
2020
MutationSettledListener,
2121
} from './mutation.types';
22+
import { QueryClient } from './query-client';
2223
import { AnyQueryClient, QueryClientHooks } from './query-client.types';
2324

2425
const originalMutationProperties = [
@@ -130,31 +131,34 @@ export class Mutation<
130131
) {
131132
const { queryClient, invalidateQueries, mutationFn, ...restOptions } =
132133
config;
134+
135+
// simple type override to make typescript happy
136+
// and do less for javascript
137+
const qc = queryClient as unknown as Partial<
138+
Pick<QueryClient, 'mutationFeatures' | 'hooks'>
139+
>;
140+
133141
this.abortController = new LinkedAbortController(config.abortSignal);
134142
this.queryClient = queryClient;
135143
this.result = undefined as any;
144+
136145
this.features = {
137-
invalidateByKey: config.invalidateByKey,
138-
lazy: config.lazy,
139-
resetOnDestroy: config.resetOnDestroy ?? config.resetOnDestroy,
140-
transformError: config.transformError,
146+
invalidateByKey:
147+
config.invalidateByKey ?? qc.mutationFeatures?.invalidateByKey,
148+
lazy: config.lazy ?? qc.mutationFeatures?.lazy,
149+
resetOnDestroy:
150+
config.resetOnDestroy ??
151+
config.resetOnDispose ??
152+
qc.mutationFeatures?.resetOnDestroy ??
153+
qc.mutationFeatures?.resetOnDispose,
154+
transformError:
155+
config.transformError ?? qc.mutationFeatures?.transformError,
141156
};
157+
142158
this.settledListeners = [];
143159
this.errorListeners = [];
144160
this.doneListeners = [];
145161

146-
if ('mutationFeatures' in queryClient) {
147-
this.features.invalidateByKey ??=
148-
queryClient.mutationFeatures.invalidateByKey;
149-
this.features.lazy ??= queryClient.mutationFeatures.lazy;
150-
this.features.resetOnDestroy ??=
151-
queryClient.mutationFeatures.resetOnDestroy ??
152-
queryClient.mutationFeatures.resetOnDispose;
153-
this.features.transformError ??= queryClient.queryFeatures.transformError;
154-
155-
this.hooks = queryClient.hooks;
156-
}
157-
158162
observable.deep(this, 'result');
159163
action.bound(this, 'updateResult');
160164
this.mutate = this.mutate.bind(this);

src/query.ts

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -286,27 +286,33 @@ export class Query<
286286
this.errorListeners = [];
287287
this.doneListeners = [];
288288

289+
// simple type override to make typescript happy
290+
// and do less for javascript
291+
const qc = queryClient as unknown as Partial<
292+
Pick<QueryClient, 'queryFeatures' | 'hooks'>
293+
>;
294+
289295
this.features = {
290-
cumulativeQueryHash: config.cumulativeQueryHash,
291-
enableOnDemand: config.enableOnDemand,
292-
lazy: config.lazy,
293-
resetOnDestroy: config.resetOnDestroy,
294-
removeOnDestroy: config.removeOnDestroy,
295-
transformError: config.transformError,
296-
dynamicOptionsUpdateDelay: config.dynamicOptionsUpdateDelay,
296+
cumulativeQueryHash:
297+
config.cumulativeQueryHash ?? qc.queryFeatures?.cumulativeQueryHash,
298+
enableOnDemand: config.enableOnDemand ?? qc.queryFeatures?.enableOnDemand,
299+
lazy: config.lazy ?? qc.queryFeatures?.lazy,
300+
resetOnDestroy:
301+
config.resetOnDestroy ??
302+
config.resetOnDispose ??
303+
qc.queryFeatures?.resetOnDestroy ??
304+
qc.queryFeatures?.resetOnDispose,
305+
removeOnDestroy:
306+
config.removeOnDestroy ?? qc.queryFeatures?.removeOnDestroy,
307+
transformError: config.transformError ?? qc.queryFeatures?.transformError,
308+
dynamicOptionsUpdateDelay:
309+
config.dynamicOptionsUpdateDelay ??
310+
qc.queryFeatures?.dynamicOptionsUpdateDelay,
311+
autoRemovePreviousQuery:
312+
config.autoRemovePreviousQuery ??
313+
qc.queryFeatures?.autoRemovePreviousQuery,
297314
};
298-
299-
if ('queryFeatures' in queryClient) {
300-
this.features.lazy ??= queryClient.queryFeatures.lazy;
301-
this.features.enableOnDemand ??=
302-
queryClient.queryFeatures.enableOnDemand ??
303-
queryClient.queryFeatures.resetOnDispose;
304-
this.features.cumulativeQueryHash ??=
305-
queryClient.queryFeatures.cumulativeQueryHash;
306-
this.features.transformError ??= queryClient.queryFeatures.transformError;
307-
308-
this.hooks = queryClient.hooks;
309-
}
315+
this.hooks = qc.hooks;
310316

311317
observable.deep(this, '_result');
312318
observable.ref(this, 'isResultRequsted');
@@ -537,7 +543,18 @@ export class Query<
537543
private processOptions(
538544
options: QueryOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey>,
539545
) {
540-
options.queryHash = this.createQueryHash(options.queryKey, options);
546+
const nextQueryHash = this.createQueryHash(options.queryKey, options);
547+
548+
if (
549+
this.features.autoRemovePreviousQuery &&
550+
options.queryHash !== nextQueryHash
551+
) {
552+
this.queryClient.removeQueries({
553+
predicate: (query) => query.queryHash === options.queryHash,
554+
});
555+
}
556+
557+
options.queryHash = nextQueryHash;
541558

542559
if (this.features.cumulativeQueryHash) {
543560
this.cumulativeQueryKeyHashesSet.add(options.queryHash);
@@ -631,6 +648,17 @@ export class Query<
631648
});
632649
}
633650

651+
if (params?.safe) {
652+
return this.queryClient.removeQueries({
653+
...params,
654+
predicate: (query) =>
655+
query.queryHash === this.options.queryHash &&
656+
(query.observers.length === 0 ||
657+
(query.observers.length === 1 &&
658+
query.observers[0] === this.queryObserver)),
659+
});
660+
}
661+
634662
return this.queryClient.removeQueries({
635663
queryKey: this.options.queryKey,
636664
exact: true,
@@ -677,7 +705,9 @@ export class Query<
677705
}
678706

679707
if (this.features.removeOnDestroy) {
680-
this.remove();
708+
this.remove({
709+
safe: this.features.removeOnDestroy === 'safe',
710+
});
681711
}
682712

683713
delete this._observerSubscription;

src/query.types.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,12 @@ export interface QueryResetParams
2222
extends Partial<Omit<QueryFilters, 'queryKey' | 'exact' | 'predicate'>> {}
2323

2424
export interface QueryRemoveParams
25-
extends Partial<Omit<QueryFilters, 'queryKey' | 'exact' | 'predicate'>> {}
25+
extends Partial<Omit<QueryFilters, 'queryKey' | 'exact' | 'predicate'>> {
26+
/**
27+
* Removes only queries that have no observers or one observer that is `queryObserver`.
28+
*/
29+
safe?: boolean;
30+
}
2631

2732
/**
2833
* @deprecated ⚠️ use `QueryResetParams`. This type will be removed in next major release
@@ -117,9 +122,11 @@ export interface QueryFeatures {
117122
/**
118123
* Removes query when destroy or abort signal is called
119124
*
125+
* `safe` - means removes only queries that have no observers or one observer that is `queryObserver`. @see QueryRemoveParams
126+
*
120127
* It uses [queryClient.removeQueries](https://tanstack.com/query/v5/docs/reference/QueryClient#queryclientremovequeries)
121128
*/
122-
removeOnDestroy?: boolean;
129+
removeOnDestroy?: boolean | 'safe';
123130

124131
/**
125132
* Enable query only if result is requested
@@ -145,6 +152,11 @@ export interface QueryFeatures {
145152
* When destroy happened all accumulated query keys will be removed (if removeOnDestroy is true), and reseted (if resetOnDestroy is true)
146153
*/
147154
cumulativeQueryHash?: boolean;
155+
156+
/**
157+
* Removes previous query if current query hash is different
158+
*/
159+
autoRemovePreviousQuery?: boolean;
148160
}
149161

150162
/**

0 commit comments

Comments
 (0)