Skip to content

bug(svelte): useAskable.svelte.ts registers listeners without $effect — no automatic cleanup on unmount #267

Closed

Description

@vamgan

Summary

In packages/svelte/src/useAskable.svelte.ts, focus and clear event listeners are registered synchronously at the top level of the function body, outside any $effect. A destroy() function exists to remove them, but callers must invoke it manually — typically in onDestroy. If they forget, listeners accumulate.

Current code

ctx.on('focus', handleFocus);   // registered at call time, not in $effect
ctx.on('clear', handleClear);

Cleanup only happens if the caller explicitly calls destroy():

function destroy() {
  ctx.off('focus', handleFocus);
  ctx.off('clear', handleClear);
  if (!usesProvidedCtx) ctx.destroy();
}

Expected behaviour

Svelte 5 runes expect side effects in $effect blocks so the runtime manages cleanup:

$effect(() => {
  const handleFocus = (f: AskableFocus) => { focus = f; };
  const handleClear = () => { focus = null; };
  ctx.on('focus', handleFocus);
  ctx.on('clear', handleClear);
  return () => {
    ctx.off('focus', handleFocus);
    ctx.off('clear', handleClear);
  };
});

destroy() can then be retained for explicit ctx teardown (unobserve, etc.) without needing to also handle listener cleanup.

Impact

Memory leak in any Svelte 5 app that unmounts components using useAskable without calling .destroy() explicitly.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: svelteSvelte adapter workbugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions