Angular 연동
Angular adapter는 injection context 또는 명시적인 DestroyRef로 cleanup을 제공할 수 있는 위치에서 사용하세요. @ilokesto/state/angular에서 import합니다.
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는 { 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' });
}Angular 생명주기 밖에서 사용하기
const count = useCounter.readOnly((state) => state.count);
const dispatch = useCounter.writeOnly();
dispatch({ type: 'reset' });주의할 점
{ destroyRef }가 없으면 adapter는inject(DestroyRef)를 호출하므로 injection context가 필요합니다.- cleanup은
destroyRef.onDestroy(unsubscribe)에 등록됩니다. - adapter result는 low-level integration을 위해
subscribe도 노출하지만 template에서는 signal을 우선 사용하세요.