Krótki opis
Poznaj, jak NgRx pomaga zarządzać złożonym stanem aplikacji w Angularze, wykorzystując architekturę inspirowaną Reduxem. Ta prezentacja wyjaśnia koncepcje NgRx, takie jak Store, Actions, Reducers, Effects i Selectors — z rzeczywistymi przykładami, które pomogą Ci pisać skalowalny i łatwy w utrzymaniu kod.
Krótki podsumowanie
W miarę rozrastania się aplikacji Angular, zarządzanie współdzielonym stanem staje się coraz większym wyzwaniem. W tej prezentacji zagłębimy się w NgRx — reaktywną bibliotekę do zarządzania stanem dla Angulara, opartą na wzorcu Redux. Dowiesz się:
- Czym jest zarządzanie stanem i dlaczego jest kluczowe we współczesnych aplikacjach
- Podstawowe koncepcje NgRx: Store, Actions, Reducers, Selectors i Effects
- Jak skonfigurować i uporządkować NgRx w skalowalnym projekcie Angular
- Dobre praktyki i typowe pułapki
- Praktyczne przykłady użycia NgRx do zarządzania stanem UI, wywołaniami API i nie tylko
Po zakończeniu sesji będziesz mieć jasne zrozumienie, jak skutecznie korzystać z NgRx, aby budować solidne, łatwe w utrzymaniu i skalowalne aplikacje Angular.
Wprowadzenie:
W miarę jak aplikacje Angular stają się coraz bardziej złożone, zarządzanie współdzielonym stanem między komponentami staje się poważnym wyzwaniem. NgRx, potężna biblioteka do zarządzania stanem dla Angulara inspirowana wzorcem Redux, oferuje solidne rozwiązanie. Dzięki niedawnemu wprowadzeniu sygnałów w Angularze, NgRx ewoluował, oferując bardziej uproszczone i nowoczesne podejście do zarządzania stanem, które jest zarówno bardzo wydajne, jak i przyjazne dla dewelopera. Ten nowy paradygmat, oparty na bibliotece @ngrx/signals i jej SignalStore, upraszcza architekturę, zachowując jednocześnie kluczowe zasady przewidywalnego zarządzania stanem.
Czym jest zarządzanie stanem i dlaczego jest kluczowe we współczesnych aplikacjach
We współczesnych aplikacjach internetowych wiele komponentów często musi uzyskiwać dostęp do tych samych danych i je modyfikować. Bez scentralizowanej strategii może to prowadzić do splątanej sieci przepływów danych, co sprawia, że aplikacja staje się trudna do debugowania, utrzymania i skalowania. Biblioteki do zarządzania stanem, takie jak NgRx, zapewniają jedno źródło prawdy dla stanu aplikacji, gwarantując, że dane przepływają w przewidywalny, jednokierunkowy sposób. Ta przewidywalność jest kluczowa dla budowania solidnych i łatwych w utrzymaniu aplikacji.
Podstawowe koncepcje nowoczesnego NgRx z użyciem sygnałów
Klasyczna architektura NgRx, z wyraźnie zdefiniowanymi Actions, Reducers, Effects i Selectors, została uproszczona dzięki wprowadzeniu @ngrx/signals. Chociaż podstawowe zasady pozostały bez zmian, ich implementacja jest teraz bardziej zgodna z reaktywnym systemem sygnałów Angulara.
Klasyczne podejście w NgRx | NgRx z Sygnałami (SignalStore) | Opis |
Store | SignalStore | Pojedyncze, scentralizowane źródło prawdy dla stanu aplikacji. Obecnie jest tworzone za pomocą funkcji signalStore |
Actions | Metody withMethods | W nowym modelu wyraźne wywołania akcji są często zastępowane przez bezpośrednie wywoływanie metod na store. Metody te opisują unikalne zdarzenia, które mogą prowadzić do zmian stanu lub efektów ubocznych. |
Reducers | withState & patchState | Stan początkowy jest definiowany za pomocą narzędzia withState. Zmiany stanu są obsługiwane przez metody wykorzystujące patchState do niemutowalnej aktualizacji stanu. |
Selectors | withComputed | Są to funkcje, które wyprowadzają i zapamiętują dane ze stanu. withComputed pozwala na tworzenie sygnałów pochodnych, które automatycznie aktualizują się, gdy zmienia się stan, od którego zależą. |
Effects | rxMethod & metody w withMethods | Efekty poboczne, takie jak asynchroniczne wywołania API, są teraz głównie zarządzane wewnątrz metod store, często z użyciem narzędzia rxMethod do integracji z RxJS. Pozwala to na umieszczenie akcji i jej odpowiadającego efektu ubocznego w jednym miejscu. |
Strukturyzowanie skalowalnego projektu NgRx z użyciem sygnałów
Kluczową zaletą SignalStore jest możliwość umieszczenia powiązanego stanu, updaterów i efektów często w jednym pliku store specyficznym dla danej funkcji. Sprzyja to lepszej organizacji i modularności.
Typowa struktura może obejmować:
- Stories oparte na funkcjach: Zamiast jednego globalnego store, aplikacje często składają się z wielu mniejszych instancji SignalStore, z których każda zarządza stanem konkretnej funkcji (np. products.store.ts, cart.store.ts).
- Providowanie Store: SignalStore może być dostarczany na poziomie root aplikacji lub na poziomie komponentu, w zależności od zakresu jego użycia.
- Interakcja z komponentami: Komponenty wstrzykują odpowiedni store i komunikują się z nim, wywołując jego metody, aby wywołać zmiany stanu, oraz subskrybując jego sygnały stanu, aby reagować na aktualizacje.
Rzućmy okiem na kod: realny przykład użycia
Rozważmy prostą funkcję licznika, aby zilustrować koncepcje:
konfiguracja:
ng add @ngrx/store
To polecenie instaluje główny pakiet NgRx store. Następnie zainstaluj pakiet sygnałów:
npm install @ngrx/signals
Te dwa pakiety, @ngrx/store i @ngrx/signals, to wszystko, czego potrzebujesz, aby rozpocząć pracę z nowoczesnym podejściem opartym na sygnałach.
Skonfigurój swój pierwszy SignalStore
Konfiguracja SignalStore jest prosta. Definiujesz kształt store, stan oraz metody w jednym pliku.
Krok 1: Stwórz plik Store
Utwórz nowy plik dla swojego store, na przykład counter.store.ts. Ten plik będzie zawierał całą logikę zarządzania stanem licznika.
Krok 2: Zdefiniuj Store
Wewnątrz pliku counter.store.ts użyj funkcji signalStore, aby zdefiniować strukturę store. Do jego budowy wykorzystasz pomocnicze funkcje takie jak withState i withMethods.
counter.store.ts
import { signalStore, withState, withMethods, patchState } from '@ngrx/signals';
// Define the shape of the state
export interface CounterState {
count: number;
}
// Set the initial state
const initialState: CounterState = {
count: 0,
};
// Create the SignalStore
export const CounterStore = signalStore(
{ providedIn: 'root' }, // Makes the store available app-wide
withState(initialState), // Adds the state to the store
// Adds methods to update the state
withMethods((store) => ({
increment() {
// Use patchState to immutably update the state
patchState(store, { count: store.count() + 1 });
},
decrement() {
patchState(store, { count: store.count() - 1 });
},
reset() {
patchState(store, initialState);
},
}))
);
Kluczowe części tej konfiguracji:
- signalStore: Główna funkcja do tworzenia store.
- { providedIn: 'root’ }: Sprawia, że CounterStore jest serwisem singletonem, który można wstrzykiwać w dowolnym miejscu aplikacji.
- withState(initialState): Definiuje stan początkowy store.
- withMethods(…): Definiuje metody, które można wywoływać do interakcji ze store.
- patchState(…): Funkcja używana wewnątrz metod do bezpiecznej i niemutowalnej aktualizacji stanu.
Krok 4: Użyć Store w komponencie
Teraz możesz wstrzyknąć i użyć twojego CounterStore w komponencie.
counter.component.ts
import { Component, inject } from '@angular/core';
import { CounterStore } from './counter.store';
@Component({
selector: 'app-counter',
standalone: true, // Make sure it's a standalone component
template: `
<h2>Counter: {{ store.count() }}</h2>
<button (click)="store.increment()">Increment</button>
<button (click)="store.decrement()">Decrement</button>
<button (click)="store.reset()">Reset</button>
`,
})
export class CounterComponent {
// Inject the store directly into the component
readonly store = inject(CounterStore);
}
Twój komponent teraz odczytuje stan bezpośrednio z sygnału store.count() i wywołuje metody takie jak store.increment(), aby go aktualizować. Widok automatycznie reaguje na wszelkie zmiany stanu. W zasadzie w tym przykładzie CounterStore definiuje stan początkowy oraz metody do jego modyfikacji. CounterComponent wstrzykuje store i bezpośrednio wywołuje te metody w odpowiedzi na interakcje użytkownika. Szablon natomiast reaktywnie wyświetla sygnał count ze store.
Najlepsze praktyki i powrzechne pułapki
- Stosuj niemutowalność: Zawsze traktuj stan jako niemutowalny. Funkcja patchState pomaga to wymusić, tworząc nowy obiekt stanu z zaktualizowanymi wartościami.
- Utrzymuj sklepy skupione: Unikaj tworzenia jednego monolitycznego store. Zamiast tego podziel stan aplikacji na logiczne, oparte na funkcjach sklepy.
- Wykorzystuj withComputed: Do danych pochodnych używaj withComputed, aby tworzyć zapamiętane selektory. Zapobiega to zbędnym obliczeniom i poprawia wydajność.
- Obsługuj efekty uboczne w metodach: Umieszczaj asynchroniczne operacje wewnątrz metod store, korzystając z narzędzi takich jak rxMethod. Ułatwia to śledzenie przepływu danych.
- Unikaj nadmiernego pobierania danych: Projektuj stan i selektory tak, aby pobierały tylko dane potrzebne komponentom, zapobiegając niepotrzebnym ponownym renderowaniom.
Dzięki wykorzystaniu NgRx z sygnałami możesz budować aplikacje Angular, które są nie tylko skalowalne i wydajne, ale także bardziej intuicyjne i łatwe w utrzymaniu, skutecznie opanowując złożoność współczesnego stanu aplikacji.
Uwaga: Aby właściwie opanować i wdrożyć omawiane podejście do zarządzania stanem, powinieneś korzystać z Angulara w wersji 17 lub nowszej, stabilnej.
Aby dowiedzieć się więcej o SignalStore i NgRx, możesz również zapoznać się z tymi artykułami! ->