createServiceMixin
createServiceMixin<Instance, Target, Options extends object = object>(
definition: ServiceMixinDefinition<Target, Options>,
): ServiceMixin<Instance, Target, Options>Builds the mixin-and-decorator pair over a service. Every built-in with* is one of these.
The definition
interface ServiceMixinDefinition<Target, Options> {
hook: string;
target: (instance: Base) => Target;
defaultImmediate?: boolean;
use: (target: Target, options: Options) => Service<unknown, unknown>;
handleResult?: (instance: Base, result: unknown) => void;
}| Field | Does |
|---|---|
hook | the one method name the service owns |
target | the default target resolver |
defaultImmediate | whether the first delivery is asked for unless the caller says otherwise |
use | calls the service factory |
handleResult | does something with what the hook returned — withRaf schedules a render |
Usage
interface FocusHook {
focused?(props: FocusProps): void;
}
export const withFocus = createServiceMixin<FocusHook, Element>({
hook: 'focused',
target: (instance) => instance.$el,
use: (target) => useFocus(target),
});class Field extends withFocus(Base) {
focused({ hasFocus }) {}
}use receives the service's options only
target, manual and immediate are removed before use is called and they are absent from its Options type — which is why use: (target, options) => useDrag(target, options) is correct and needs no filtering.
Override $mount(), not mounted()
createServiceMixin() overrides $mount() and $unmount(), which is what lets a component write its own mounted() without super.mounted() and still subscribe.
The rule is about what a mixin overrides, not who wrote it. A userland mixin that puts its work in mounted() needs its subclasses to call super.mounted(). The way not to need that is to override $mount() — or to use this function, which already does.
What it gives the class
- the hook, bound for each mount cycle;
$services.<hook>as aToggle, typed throughServiceHandles<'<hook>'>;- the two call forms, mixin and class decorator;
- the
service.missing-targetcheck on a caller-supplied resolver.
One hook per class
A mixin names one hook and binds one subscription per cycle. That is its limit, and a component with a dynamic set of subscriptions calls subscribe() itself. See Mixins.