Proste zarządzanie stanem na sygnałach w Angularze
Dość często w swoich aplikacjach niepotrzebujemy zaciągać zaawansowanych bibliotek do zarządzania stanem takich jak NgRx czy NGXS.
Niestety wielu niedoświadczonych programistów lub doświadczonych, ale którzy nie mieli nigdy styczności z zaawansowanym stanem (ale czy możemy ich nazwać doświadczonymi? ( ͡° ͜ʖ ͡°)) ulega pokusie instalowania JUŻ na starcie potężnej libki do stanu, bo przecież każdy chce być fajny, nowoczesny no i w internecie tak ładnie o tym piszą!
Moja rada
Zacznij projekt od prostych rozwiązań. Nie wiadomo jak się aplikacja rozwinie w czasie. Czasami projekt jest planowany na lata, a kończy się po pół roku, bo biznesowo spełnia wszystko i zostaje z pięcioma widokami, czasem zaś aplikacja ma być na trzy widoki i dwa miesiące, a projekt trwa 10 lat.
Na to wpływa wiele czynników, może:
- dane będą na bieżąco aktualizowane z backendu, wtedy taki stan wręcz przeszkadza swoimi narzutami,
- będzie dużo formularzy i wystarczy trzymać dane w Reactive Forms czy nadchodzących Signal Forms?
Na początku skup się na ogólnej architekturze projektu, wzorcach, jak i dokumentacji Angulara z minimalnym instalowaniem zewnętrznych bibliotek. Naprawdę Angular jest potężny ma wiele w sobie fajnych, ciekawych rozwiązań, jeśli pomyślisz o jakimś rozwiązaniu prawdopodobnie Angular ma to już w sobie!
Kilka lat temu miałem podobnie potrzebowałem zrobić coś na zasadzie przewijania do sekcji. Miałem prosty landing page z menu, przy scrollowaniu myszką do sekcji chciałem, aby menu mi się pozycja w menu zrobiła aktywna.
Pomyślałem, że to jest dość generyczne rozwiązanie może Angular ma coś takiego wbudowanego? Szybki research i pyk klasa ViewportScroller rozwiązała problem!
Od tamtej pory zmieniłem podejście, najpierw dogłębnie szukam czy jest wbudowane rozwiązanie jeszcze teraz w dobie AI gdzie wystarczy wpisać i dostajemy wszystko w minutę.
A teraz do rzeczy!
Prosty stan
Pokażę Ci kompletne rozwiązanie prostego stanu do trzymania danych czy to jakiś token, id, a nawet pokusiłbym się o przetrzymywanie prostych danych np. lista krajów, prefixy numerów telefonów itp.
Zgodne z najlepszymi praktykami Angulara i DI!
Na początku zróbmy token o nazwie STATE który będziemy wstrzykiwać w nasze komponenty oraz interfejsy stanu:
//state.token.ts
export const STATE = new InjectionToken<ApplicationState>('Application State');
//state.interface.ts
export interface ApplicationState {
data: Signal<DataApplicationState>;
update: (updateState: Partial<DataApplicationState>) => void;
reset: () => void;
}
//przykładowy kontrakt przetrzymywanych danych
export interface DataApplicationState {
appId: number | null;
accessToken: string | null;
}
Następnie stworzymy fabrykę:
//state.factory.ts
export function applicationState(): ApplicationState {
const data = signal(stateDefault());
return {
data,
update: (updateState) =>
data.update((current) => ({ ...current, ...updateState })),
reset: () => data.set(stateDefault()),
};
}
const stateDefault = (): DataApplicationState => ({
appId: null,
accessToken: null,
});
Na końcu musimy zarejestrować nasz token:
bootstrapApplication(App, {
providers: [
{
provide: STATE,
useFactory: applicationState,
},
],
});
I to w sumie tyle, jeśli chodzi o implementację.
Prawda, że proste?!
Jak to wykorzystać?
Jeśli chcemy wykorzystać lub zaktualizować stan wystarczy w komponencie wstrzyknąć nasz token jak poniżej:
@Component({
template: `
{{ state.data().appId }}
<button (click)="update()">Klik</button>
<button (click)="reset()">Reset</button>
`,
})
export class App {
readonly state = inject(STATE);
update(): void {
this.state.update({ appId: Math.random() });
}
reset(): void {
this.state.reset();
}
}
Całość można przetestowąć tutaj.
Weź, implementuj, rozwijaj według swoich potrzeb… i daj znać!