import { effectScope, type EffectScope } from "vue"; /** * Creates a constructor for a class that may contain vue reactive primitives, setting up an effect scope to capture * all effects, assigning a destroy function that will stop the effect scope and clean up effects. * * Wrapped classes containing reactive effects can be easily used without tying their lifetimes to a component instance * or the global application. This is effectively the equivalent of heap allocation instead of stack allocation. * * Advantages: * - less boilerplate (no need to remember to return all public methods and properties from a composable) * - makes it possible to manange reactive lifetimes that are entirely independent of UI state, such as objects that * are constantly instantiated and destroyed that manage their own reactive states, that need to be referenced * globally by multiple parts of a complex UI. * - The class syntax makes it easy to reference properites and functions before their declaration, avoiding annoying * forward declarations. */ export default class EffectScoped { private effectScope?: EffectScope; /** * Creates an effectscoped version of the class */ static asScoped EffectScoped>(this: C, ...args: ConstructorParameters): InstanceType { const scope = effectScope(true); const instance = scope.run(() => new this(...args)) ?? null; if (instance === null) { throw new Error(`Failed to instantiate class ${ this.constructor.name }.`); } instance.effectScope = scope; return instance as InstanceType; } onDestroy() {} destroy() { this.onDestroy(); this.effectScope?.stop(); } }