diff --git a/.changeset/refactor-destroyable-internals.md b/.changeset/refactor-destroyable-internals.md new file mode 100644 index 0000000..e367406 --- /dev/null +++ b/.changeset/refactor-destroyable-internals.md @@ -0,0 +1,22 @@ +--- +"mobx-tanstack-query": major +--- + +### Breaking changes + +- **Removed `protected abortController`** from `Query`, `InfiniteQuery`, and `Mutation`. Use `this._abortSignal` (can be `undefined`) and `this._destroyed` instead. + +- **Removed `handleDestroy()`**. Override `destroy()` and call `super.destroy()` at the top: + + ```ts + // Before + protected handleDestroy() { ... } + + // After + destroy() { + super.destroy(); + // your cleanup + } + ``` + +- **`Destroyable` no longer calls `makeObservable(this)`**. Call it explicitly in your subclass constructor if needed. diff --git a/package.json b/package.json index 98ab1cc..33130a7 100644 --- a/package.json +++ b/package.json @@ -49,10 +49,10 @@ "mobx": "^6.12.4" }, "dependencies": { - "linked-abort-controller": "^1.1.1", "yummies": "^7.19.4" }, "devDependencies": { + "linked-abort-controller": "^1.1.1", "@biomejs/biome": "^2.4.14", "@changesets/changelog-github": "^0.6.0", "@changesets/cli": "^2.31.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 140693e..790d3d5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,9 +11,6 @@ importers: '@tanstack/query-core': specifier: ^5.90.2 version: 5.100.7 - linked-abort-controller: - specifier: ^1.1.1 - version: 1.1.1 mobx: specifier: ^6.12.4 version: 6.15.0 @@ -60,6 +57,9 @@ importers: lefthook: specifier: ^1.13.6 version: 1.13.6 + linked-abort-controller: + specifier: ^1.1.1 + version: 1.1.1 nodemon: specifier: ^3.1.14 version: 3.1.14 diff --git a/src/base-query.ts b/src/base-query.ts index 83c1168..ed20f17 100644 --- a/src/base-query.ts +++ b/src/base-query.ts @@ -317,7 +317,7 @@ export abstract class BaseQuery< * [**Documentation**](https://js2me.github.io/mobx-tanstack-query/api/Query.html#update-options) */ update(optionsUpdate: TUpdateOptions): void { - if (this.abortController.signal.aborted) { + if (this._destroyed) { return; } @@ -388,7 +388,7 @@ export abstract class BaseQuery< } this.update({} as TUpdateOptions); } - if (this.abortController.signal.aborted && this._result) { + if (this._destroyed && this._result) { return this.queryObserver.getOptimisticResult(this.options); } return this._result || this.queryObserver.getCurrentResult(); @@ -587,7 +587,7 @@ export abstract class BaseQuery< if (this.result.isFetching) { await when(() => !this.result.isFetching, { - signal: this.abortController.signal, + signal: this._abortSignal, }); const throwableError = this.getCurrentThrowableError(); if (throwableError) { diff --git a/src/inifinite-query.ts b/src/inifinite-query.ts index a6f9309..bc953ad 100644 --- a/src/inifinite-query.ts +++ b/src/inifinite-query.ts @@ -357,7 +357,8 @@ export class InfiniteQuery< return this._result.fetchPreviousPage(options); } - protected handleDestroy() { + destroy(): void { + super.destroy(); this.cleanup(); this.hooks?.onInfiniteQueryDestroy?.(this); } diff --git a/src/mutation.ts b/src/mutation.ts index d87faf6..89b7321 100644 --- a/src/mutation.ts +++ b/src/mutation.ts @@ -139,6 +139,9 @@ export class Mutation< isPaused!: boolean; submittedAt!: number; + /** */ + protected tempAc?: AbortController; + constructor( protected config: MutationConfig< TData, @@ -193,10 +196,14 @@ export class Mutation< // @ts-expect-error >(queryClient, { ...this.mutationOptions, + onSettled: (...args) => { + this.abortTempAc(); + return this.mutationOptions.onSettled?.(...args); + }, mutationFn: (variables, context) => mutationFn?.(variables, { ...context, - signal: this.abortController.signal, + signal: this._abortSignal ?? this.recreateTempAc().signal, } satisfies MutationFunctionContext), }); @@ -386,7 +393,22 @@ export class Mutation< this.mutationObserver.reset(); } - protected handleDestroy() { + protected abortTempAc() { + this.tempAc?.abort(); + this.tempAc = undefined; + } + + protected recreateTempAc() { + this.abortTempAc(); + this.tempAc = new AbortController(); + return this.tempAc; + } + + destroy() { + super.destroy(); + + this.abortTempAc(); + this._observerSubscription?.(); this.doneListeners = []; diff --git a/src/query.ts b/src/query.ts index 1656929..1497720 100644 --- a/src/query.ts +++ b/src/query.ts @@ -354,7 +354,8 @@ export class Query< this.hooks?.onQueryInit?.(this); } - protected handleDestroy() { + destroy(): void { + super.destroy(); this.cleanup(); this.hooks?.onQueryDestroy?.(this); } diff --git a/src/utils/destroyable.ts b/src/utils/destroyable.ts index ec0451e..5a7461e 100644 --- a/src/utils/destroyable.ts +++ b/src/utils/destroyable.ts @@ -1,26 +1,24 @@ -import { LinkedAbortController } from 'linked-abort-controller'; -import { action, makeObservable } from 'mobx'; - export abstract class Destroyable implements Disposable { - protected abortController: LinkedAbortController; + protected _abortSignal?: AbortSignal; + protected _destroyed: boolean; constructor(abortSignal?: AbortSignal) { - this.abortController = new LinkedAbortController(abortSignal); - - action(this, 'handleDestroy'); - makeObservable(this); - - this.abortController.signal.addEventListener('abort', () => { - this.handleDestroy(); - }); + this._abortSignal = abortSignal; + this._destroyed = false; + + this._abortSignal?.addEventListener( + 'abort', + () => { + this.destroy(); + }, + { once: true }, + ); } destroy() { - this.abortController?.abort(); + this._destroyed = true; } - protected abstract handleDestroy(): void; - [Symbol.dispose](): void { this.destroy(); }