-
-
Notifications
You must be signed in to change notification settings - Fork 2
[refactor!]: removed protected abortController + _handleDestory methods… #82
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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. |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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) => { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: Race condition: when multiple mutations are triggered concurrently (without Prompt for AI agents |
||
| 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), | ||
| }); | ||
|
Comment on lines
+199
to
208
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: Yes—MutationObserver (and thus Citations:
A safer approach is to track the controller per in-flight call (e.g., capture it in a closure inside 🤖 Prompt for AI Agents |
||
|
|
||
|
|
@@ -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 = []; | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -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( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Two lifecycle edge cases:
Consider checking Prompt for AI agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'abort', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.destroy(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { once: true }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| destroy() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.abortController?.abort(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this._destroyed = true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
5
to
20
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Two lifecycle edge cases worth handling explicitly.
🛡️ Proposed fix — guard idempotency and handle pre-aborted signals constructor(abortSignal?: AbortSignal) {
this._abortSignal = abortSignal;
this._destroyed = false;
- this._abortSignal?.addEventListener(
- 'abort',
- () => {
- this.destroy();
- },
- { once: true },
- );
+ if (abortSignal?.aborted) {
+ // Defer so subclass constructors finish before destroy() runs.
+ queueMicrotask(() => this.destroy());
+ } else {
+ abortSignal?.addEventListener(
+ 'abort',
+ () => this.destroy(),
+ { once: true },
+ );
+ }
}
destroy() {
+ if (this._destroyed) return;
this._destroyed = true;
}The early-return in 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| protected abstract handleDestroy(): void; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [Symbol.dispose](): void { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.destroy(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P1:
this._abortSignalcan beundefinedwhen no external abort signal is provided to the constructor. This meanswhen()won't be cancelled ondestroy(), leaving a dangling observation/promise. The mutation class handles this withthis._abortSignal ?? this.recreateTempAc().signalas a fallback.Prompt for AI agents