Files
arne-drums/src/EffectScoped.ts
Daniel Ledda 4268cec832 update
2023-05-07 21:14:48 +02:00

42 lines
1.7 KiB
TypeScript

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<C extends new(...args: any[]) => EffectScoped>(this: C, ...args: ConstructorParameters<C>): InstanceType<C> {
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<C>;
}
onDestroy() {}
destroy() {
this.onDestroy();
this.effectScope?.stop();
}
}