Angular
Use the Angular adapter where Angular can provide cleanup through an injection context or explicit DestroyRef. Import from @ilokesto/state/angular.
Plain state signal
import { Component, DestroyRef, inject } from '@angular/core';
import { create } from '@ilokesto/state/angular';
const useCounter = create({ count: 0, label: 'Clicks' });
@Component({ selector: 'counter-view', template: `<button (click)="inc()">{{ label() }}: {{ count() }}</button>` })
export class CounterView {
private destroyRef = inject(DestroyRef);
private countResult = useCounter((state) => state.count, { destroyRef: this.destroyRef });
private labelResult = useCounter((state) => state.label, { destroyRef: this.destroyRef });
count = this.countResult.state;
label = this.labelResult.state;
inc = () => this.countResult.setState((state) => ({ ...state, count: state.count + 1 }));
}Plain state returns { state: Signal<S>, setState, subscribe }.
Reducer signal
import { create } from '@ilokesto/state/angular';
type Action = { type: 'increment' } | { type: 'reset' };
const reduce = (state: { count: number }, action: Action) => {
if (action.type === 'increment') return { count: state.count + 1 };
if (action.type === 'reset') return { count: 0 };
return state;
};
const useCounter = create(reduce, { count: 0 });
export class ReducerCounter {
private result = useCounter((state) => state.count);
count = this.result.state;
increment = () => this.result.dispatch({ type: 'increment' });
}Outside Angular lifecycle
const count = useCounter.readOnly((state) => state.count);
const dispatch = useCounter.writeOnly();
dispatch({ type: 'reset' });Caveats
- Without
{ destroyRef }, the adapter callsinject(DestroyRef)and therefore needs an injection context. - Cleanup is registered with
destroyRef.onDestroy(unsubscribe). - The adapter result exposes
subscribefor low-level integration, but prefer signals for templates.