From b4e5f699bcae95da41010ee8464546cfad3f1aeb Mon Sep 17 00:00:00 2001 From: wmTJc9IK0Q <171362836+wmTJc9IK0Q@users.noreply.github.com> Date: Tue, 19 Aug 2025 10:14:27 -0500 Subject: [PATCH] Cancel requests when element is removed --- library/src/engine/engine.ts | 24 ++++++++++++-------- library/src/engine/types.ts | 6 ++++- library/src/plugins/backend/actions/fetch.ts | 1 + 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/library/src/engine/engine.ts b/library/src/engine/engine.ts index 2cf16cf1..a1cd1d07 100644 --- a/library/src/engine/engine.ts +++ b/library/src/engine/engine.ts @@ -4,6 +4,7 @@ import { camel, snake } from '../utils/text' import { DATASTAR, DSP, DSS } from './consts' import { initErr, runtimeErr } from './errors' import type { + ActionContext, ActionPlugins, AttributePlugin, Computed, @@ -1000,19 +1001,23 @@ function applyAttributePlugin( const cleanup = plugin.onLoad(ctx) if (cleanup) { - let cleanups = removals.get(el) - if (cleanups) { - cleanups.get(rawKey)?.() - } else { - cleanups = new Map() - removals.set(el, cleanups) - } - cleanups.set(rawKey, cleanup) + setCleanup(cleanup, el, rawKey); } } } } +function setCleanup(cleanup: OnRemovalFn, el: HTMLOrSVG, key: string): void { + let cleanups = removals.get(el) + if (cleanups) { + cleanups.get(key)?.() + } else { + cleanups = new Map() + removals.set(el, cleanups) + } + cleanups.set(key, cleanup) +} + // Set up a mutation observer to run plugin removal and apply functions function observe(mutations: MutationRecord[]) { const ignore = `[${aliasify('ignore')}]` @@ -1160,6 +1165,7 @@ function generateReactiveExpression( const actionMatches = [...expr.matchAll(actionsRe)] const actionNames = new Set() const actionFns = new Set<(...args: any[]) => any>() + const actionCtx: ActionContext = { ...ctx, setCleanup: (cleanup: OnRemovalFn, key: string) => setCleanup(cleanup, ctx.el, key) } if (actionMatches.length) { const actionPrefix = `${DATASTAR}Act_` for (const match of actionMatches) { @@ -1175,7 +1181,7 @@ function generateReactiveExpression( // Add ctx to action calls expr = expr.replace(`@${actionName}(`, `${name}(`) actionNames.add(name) - actionFns.add((...args: any[]) => action.fn(ctx, ...args)) + actionFns.add((...args: any[]) => action.fn(actionCtx, ...args)) } } diff --git a/library/src/engine/types.ts b/library/src/engine/types.ts index 99d40e8b..023f8d26 100644 --- a/library/src/engine/types.ts +++ b/library/src/engine/types.ts @@ -54,7 +54,7 @@ export type WatcherPlugin = { } export type ActionPlugins = Record -export type ActionMethod = (ctx: RuntimeContext, ...args: any[]) => any +export type ActionMethod = (ctx: ActionContext, ...args: any[]) => any export type ActionPlugin = { type: 'action' @@ -101,6 +101,10 @@ export type RuntimeContext = InitContext & { runtimeErr: (reason: string, metadata?: object) => Error } +export type ActionContext = RuntimeContext & { + setCleanup: (fn: OnRemovalFn, key: string) => void // sets a cleanup function for this element and key +} + export type RuntimeExpressionFunction = ( ctx: RuntimeContext, ...args: any[] diff --git a/library/src/plugins/backend/actions/fetch.ts b/library/src/plugins/backend/actions/fetch.ts index 5c35f0e3..e50e5a52 100644 --- a/library/src/plugins/backend/actions/fetch.ts +++ b/library/src/plugins/backend/actions/fetch.ts @@ -48,6 +48,7 @@ export const createHttpMethod = ( if (!isDisabled && !(requestCancellation instanceof AbortController)) { fetchAbortControllers.set(el, controller) + ctx.setCleanup(() => controller.abort(), `fetch`) } try {