diff --git a/src/plugins/bound/index.js b/src/plugins/bound/index.js index ebcb93d..ce8fdd6 100644 --- a/src/plugins/bound/index.js +++ b/src/plugins/bound/index.js @@ -177,7 +177,7 @@ function plugin({ directive, entangle, evaluateLater, mapAttributes, mutateDom, // will have no effect since there are no options yet. // Therefore, we use a small trick to set the value a bit later // when the "x-for" directive has finished its work. - setTimeout(() => { + const timer_id = setTimeout(() => { // if the value of the bound property is "null" or "undefined", // we initialize it with the value from the element. is_nullish(get_value()) && update_variable(); @@ -186,6 +186,10 @@ function plugin({ directive, entangle, evaluateLater, mapAttributes, mutateDom, cleanup(listen(el, "change", () => set_value(collect_selected_values(el)))); }); + // the element can be destroyed before the timer fires, + // and the effect and listener created after that would never be released. + cleanup(() => clearTimeout(timer_id)); + processed = true; break; } diff --git a/tests/playwright/x-bound.spec.js b/tests/playwright/x-bound.spec.js index 7b3674c..e546c2a 100644 --- a/tests/playwright/x-bound.spec.js +++ b/tests/playwright/x-bound.spec.js @@ -1,5 +1,5 @@ import { expect, test } from "@playwright/test"; -import { set_html } from "./assets/utils"; +import { set_data, set_html } from "./assets/utils"; import { fileURLToPath } from "url"; import path from "path"; @@ -406,6 +406,49 @@ test.describe("x-bound: select", () => { await expect(page.locator("select")).toHaveValues(["Two", "Three"]); await expect(page.locator("span")).toHaveText(`["Two","Three"]`); }); + + test("removed before the deferred binding", async ({ page }) => { + await set_html(page, ` +