Skip to content

Commit 534042e

Browse files
committed
fix: lazy queries and mutation make delayed cleanup
1 parent 340a763 commit 534042e

File tree

5 files changed

+117
-82
lines changed

5 files changed

+117
-82
lines changed

.changeset/metal-shrimps-drum.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"mobx-tanstack-query": patch
3+
---
4+
5+
fixed lazy queries cleanup functions

src/inifinite-query.ts

Lines changed: 25 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ import {
1919
makeObservable,
2020
observable,
2121
runInAction,
22-
onBecomeUnobserved,
23-
onBecomeObserved,
2422
} from 'mobx';
2523

2624
import {
@@ -35,6 +33,7 @@ import {
3533
InfiniteQueryUpdateOptionsAllVariants,
3634
} from './inifinite-query.types';
3735
import { AnyQueryClient, QueryClientHooks } from './query-client.types';
36+
import { lazyObserve } from './utils/lazy-observe';
3837

3938
const enableHolder = () => false;
4039

@@ -231,41 +230,36 @@ export class InfiniteQuery<
231230
this.updateResult(this.queryObserver.getOptimisticResult(this.options));
232231

233232
if (this.isLazy) {
234-
let dynamicOptionsDisposeFn: VoidFunction | undefined;
235-
236-
onBecomeObserved(this, '_result', () => {
237-
if (!this._observerSubscription) {
238-
if (getAllDynamicOptions) {
239-
this.update(getAllDynamicOptions());
240-
}
241-
this._observerSubscription = this.queryObserver.subscribe(
242-
this.updateResult,
243-
);
244-
if (getAllDynamicOptions) {
245-
dynamicOptionsDisposeFn = reaction(
246-
getAllDynamicOptions,
247-
this.update,
248-
{
233+
const cleanup = lazyObserve({
234+
context: this,
235+
property: '_result',
236+
onStart: () => {
237+
if (!this._observerSubscription) {
238+
if (getAllDynamicOptions) {
239+
this.update(getAllDynamicOptions());
240+
}
241+
this._observerSubscription = this.queryObserver.subscribe(
242+
this.updateResult,
243+
);
244+
if (getAllDynamicOptions) {
245+
return reaction(getAllDynamicOptions, this.update, {
249246
delay: this.config.dynamicOptionsUpdateDelay,
250247
signal: config.abortSignal,
251248
fireImmediately: true,
252-
},
253-
);
249+
});
250+
}
254251
}
255-
}
252+
},
253+
onEnd: (disposeFn, cleanup) => {
254+
if (this._observerSubscription) {
255+
disposeFn?.();
256+
this._observerSubscription();
257+
this._observerSubscription = undefined;
258+
config.abortSignal?.removeEventListener('abort', cleanup);
259+
}
260+
},
256261
});
257262

258-
const cleanup = () => {
259-
if (this._observerSubscription) {
260-
dynamicOptionsDisposeFn?.();
261-
this._observerSubscription();
262-
this._observerSubscription = undefined;
263-
dynamicOptionsDisposeFn = undefined;
264-
config.abortSignal?.removeEventListener('abort', cleanup);
265-
}
266-
};
267-
268-
onBecomeUnobserved(this, '_result', cleanup);
269263
config.abortSignal?.addEventListener('abort', cleanup);
270264
} else {
271265
if (isQueryKeyDynamic) {

src/mutation.ts

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,7 @@ import {
66
MutationOptions,
77
} from '@tanstack/query-core';
88
import { LinkedAbortController } from 'linked-abort-controller';
9-
import {
10-
action,
11-
makeObservable,
12-
observable,
13-
onBecomeObserved,
14-
onBecomeUnobserved,
15-
} from 'mobx';
9+
import { action, makeObservable, observable } from 'mobx';
1610

1711
import {
1812
MutationConfig,
@@ -23,6 +17,7 @@ import {
2317
MutationSettledListener,
2418
} from './mutation.types';
2519
import { AnyQueryClient, QueryClientHooks } from './query-client.types';
20+
import { lazyObserve } from './utils/lazy-observe';
2621

2722
export class Mutation<
2823
TData = unknown,
@@ -120,20 +115,25 @@ export class Mutation<
120115
this.updateResult(this.mutationObserver.getCurrentResult());
121116

122117
if (this.isLazy) {
123-
onBecomeObserved(this, 'result', () => {
124-
if (!this._observerSubscription) {
125-
this.updateResult(this.mutationObserver.getCurrentResult());
126-
this._observerSubscription = this.mutationObserver.subscribe(
127-
this.updateResult,
128-
);
129-
}
130-
});
131-
onBecomeUnobserved(this, 'result', () => {
132-
if (this._observerSubscription) {
133-
this._observerSubscription?.();
134-
this._observerSubscription = undefined;
135-
}
118+
const cleanup = lazyObserve({
119+
context: this,
120+
property: 'result',
121+
onStart: () => {
122+
if (!this._observerSubscription) {
123+
this.updateResult(this.mutationObserver.getCurrentResult());
124+
this._observerSubscription = this.mutationObserver.subscribe(
125+
this.updateResult,
126+
);
127+
}
128+
},
129+
onEnd: () => {
130+
if (this._observerSubscription) {
131+
this._observerSubscription?.();
132+
this._observerSubscription = undefined;
133+
}
134+
},
136135
});
136+
config.abortSignal?.addEventListener('abort', cleanup);
137137
} else {
138138
this._observerSubscription = this.mutationObserver.subscribe(
139139
this.updateResult,

src/query.ts

Lines changed: 25 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ import {
1313
action,
1414
makeObservable,
1515
observable,
16-
onBecomeObserved,
17-
onBecomeUnobserved,
1816
reaction,
1917
runInAction,
2018
} from 'mobx';
@@ -32,6 +30,7 @@ import {
3230
QueryStartParams,
3331
QueryUpdateOptionsAllVariants,
3432
} from './query.types';
33+
import { lazyObserve } from './utils/lazy-observe';
3534

3635
const enableHolder = () => false;
3736

@@ -212,41 +211,36 @@ export class Query<
212211
this.updateResult(this.queryObserver.getOptimisticResult(this.options));
213212

214213
if (this.isLazy) {
215-
let dynamicOptionsDisposeFn: VoidFunction | undefined;
216-
217-
onBecomeObserved(this, '_result', () => {
218-
if (!this._observerSubscription) {
219-
if (getAllDynamicOptions) {
220-
this.update(getAllDynamicOptions());
221-
}
222-
this._observerSubscription = this.queryObserver.subscribe(
223-
this.updateResult,
224-
);
225-
if (getAllDynamicOptions) {
226-
dynamicOptionsDisposeFn = reaction(
227-
getAllDynamicOptions,
228-
this.update,
229-
{
214+
const cleanup = lazyObserve({
215+
context: this,
216+
property: '_result',
217+
onStart: () => {
218+
if (!this._observerSubscription) {
219+
if (getAllDynamicOptions) {
220+
this.update(getAllDynamicOptions());
221+
}
222+
this._observerSubscription = this.queryObserver.subscribe(
223+
this.updateResult,
224+
);
225+
if (getAllDynamicOptions) {
226+
return reaction(getAllDynamicOptions, this.update, {
230227
delay: this.config.dynamicOptionsUpdateDelay,
231228
signal: config.abortSignal,
232229
fireImmediately: true,
233-
},
234-
);
230+
});
231+
}
235232
}
236-
}
233+
},
234+
onEnd: (disposeFn, cleanup) => {
235+
if (this._observerSubscription) {
236+
disposeFn?.();
237+
this._observerSubscription();
238+
this._observerSubscription = undefined;
239+
config.abortSignal?.removeEventListener('abort', cleanup);
240+
}
241+
},
237242
});
238243

239-
const cleanup = () => {
240-
if (this._observerSubscription) {
241-
dynamicOptionsDisposeFn?.();
242-
this._observerSubscription();
243-
this._observerSubscription = undefined;
244-
dynamicOptionsDisposeFn = undefined;
245-
config.abortSignal?.removeEventListener('abort', cleanup);
246-
}
247-
};
248-
249-
onBecomeUnobserved(this, '_result', cleanup);
250244
config.abortSignal?.addEventListener('abort', cleanup);
251245
} else {
252246
if (isQueryKeyDynamic) {

src/utils/lazy-observe.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { onBecomeObserved, onBecomeUnobserved } from 'mobx';
2+
3+
export const lazyObserve = <TMetaData = void>({
4+
context,
5+
property,
6+
onStart,
7+
onEnd,
8+
endDelay = 50,
9+
}: {
10+
context: any;
11+
property: any;
12+
onStart: () => TMetaData;
13+
onEnd: (metaData: TMetaData, cleanupFn: VoidFunction) => void;
14+
endDelay?: number;
15+
}) => {
16+
let timeoutId: ReturnType<typeof setTimeout> | undefined;
17+
let metaData: TMetaData | undefined;
18+
19+
onBecomeObserved(context, property, () => {
20+
if (timeoutId) {
21+
clearTimeout(timeoutId);
22+
timeoutId = undefined;
23+
}
24+
metaData = onStart();
25+
});
26+
27+
const cleanup = () => {
28+
if (timeoutId) {
29+
clearTimeout(timeoutId);
30+
timeoutId = undefined;
31+
}
32+
timeoutId = setTimeout(() => {
33+
onEnd(metaData!, cleanup);
34+
timeoutId = undefined;
35+
metaData = undefined;
36+
}, endDelay);
37+
};
38+
39+
onBecomeUnobserved(context, property, cleanup);
40+
41+
return cleanup;
42+
};

0 commit comments

Comments
 (0)