Wróć do strony głównej
NgRx

NgRx – tips & tricks

Na naszym blogu pojawił się już kiedyś wpis o wskazówkach do NgRx autorstwa Tomasza Nastałego, który mocno zachęcam przeczytać. Po długiej przerwie nadeszła pora na następną porcję tips & tricks – zapraszam!

1. withLatestFrom -> concatLatestFrom 

Z wpisu, który zalinkowałem we wstępie wiesz już, że gdy potrzebujesz skorzystać w efekcie z wartości znajdującej się w state, dobrym rozwiązaniem jest użycie operatora withLatestFrom. Niewiele osób jednak wie, że użycie tego operatora bez skorzystania z flattening operatora (switchMap, concatMap, mergeMap, exhaustMap) ma skutek uboczny. Operator withLatestFrom subskrybuje się, nawet jeśli główny observable w efekcie nie wyemituje wartości. Jeżeli potrzebujesz stworzyć efekt bez używania flattening operatora, zamiast withLatestFrom użyj concatLatestFrom. Dla przykładu:

Tutaj nastąpi subskrypcja selektora fromBooks.getCollectionBookIds niezależnie od tego czy akcja AddBookSuccess zostanie zdispatchowana:

 

Tutaj natomiast selektor fromBooks.getCollectionBookIds zasubskrybuje się tylko wtedy, gdy akcja AddBookSuccess zostanie zdispatchowana:

 

2. Nx DataPersistence kluczem do dobrych efektów

Nx DataPersistence dostarcza zestaw pomocniczych funkcji, które umożliwiają deweloperowi zarządzanie stanem w Angularze z uwzględnieniem strategii synchronizacji i obsługi błędu. Są to:

  • Optimistic update – w pierwszej kolejności aktualizuje dane po stronie klienta, dopiero później pozwala je synchronizować z danymi uzyskanymi z backendu. Na wypadek niepowodzenia aktualizacji danych po stronie serwera dostarcza nam handler undoAction, który pozwala na wycofanie wprowadzonych zmian po stronie klienta.
  • Pessimistic update – odwrotność powyższego. Najpierw aktualizuje dane po stronie api, a po pomyślnej operacji aktualizuje dane po stronie klienta. W razie niepowodzenia operacji po stronie serwera, nie ma potrzeby cofania wywołanych zmian, jednak na czas oczekiwania na odpowiedź serwera warto zadbać o poinformowanie użytkownika o trwającej operacji (np. przy użyciu spinnera).
  • Fetch – pobieranie danych. Ważnym elementem jest możliwość przekazania id pobieranego obiektu do efektu, dzięki czemu w razie wywoływania tej samej akcji dla różnych obiektów, zapytania wykonają się równolegle.
  • Navigate – pozwala na sprawdzenie czy aktywowana ścieżka zawiera przekazany w parametrze komponent i jeśli tak jest, wykonanie zadanego polecenia.

Nx DataPersistance bardzo ułatwia zarządzanie stanem z NgRx, przez co zachęcam do przeczytania dokumentacji i wdrożenia go do swojego projektu.

 

3. Nawigacja z użyciem NgRx

W miarę rozrastania się naszej aplikacji zarządzanie nawigacją może się skomplikować. Do ułatwienia debugowania routingu Twojej aplikacji użyj router-store’a. Od teraz store będzie dispatchował szereg akcji przy każdej uruchomionej nawigacji, co w połączeniu z Redux Devtoolsami pozwala na prześledzenie krok po kroku tego się dzieje w nawigacji.

Dodatkowo, jeżeli w projekcie korzystasz z @ngrx/entity, możesz użyć selektorów przygotowanych pod korzystanie z router-store. Dają one m.in. możliwość wybierania danych ze store w oparciu o ścieżkę url, bez konieczności pobierania informacji o route w komponencie.

 

4. Między stanem, a komponentem – fasada

Aby ułatwić organizację komponentu korzystającego ze store i poprawić czytelność kodu możesz użyć fasady. Fasada jest odpowiedzialna za utworzenie dodatkowej warstwy między komponentem, a stanem aplikacji. Możesz ją rozumieć jak API, które jest udostępnione dla komponentów przez store. Nie będę rozpisywać się o tym jak utworzyć fasadę, gdyż jest już na ten temat świetny artykuł od Thomasa Burlesona. Od siebie dodam, że po implementacji pierwszej fasady nie chciałem i nie wróciłem już nigdy do bezpośredniego używania store w komponentach.

 

5. Runtime checks

Jeżeli chcesz mieć pewność, że Twoje funkcjonalności oparte o NgRx są zgodne z jego kluczowymi konceptami, skorzystaj z runtime checks – opcji do konfiguracji store’a. Runtime checks umożliwiają sprawdzanie działania naszego kodu i w razie potrzeby informowanie o błędzie w konsoli. Jest to bardzo pomocna rzecz w trakcie developmentu. Jeżeli chcesz poznać wszystkie opcje konfiguracyjne, odsyłam Cię do świetnie napisanej dokumentacji.

 

6. Wyłuskiwanie payloadu w efekcie

Jeżeli do akcji przekazujesz obiekt i chcesz wydobyć jego wartość w efekcie, możesz użyć operatora pluck

Gdybyśmy chcieli użyć takiej akcji:

to efekt prawdopodobnie wyglądałby tak:

Z użyciem operatora pluck pozbywamy się konieczności wielokrotnego odnoszenia się do wartości bookId z action:

 

Na dzisiaj koniec!

Dajcie znać w komentarzach czy dzięki temu wpisowi dowiedzieliście się czegoś nowego i czy chcielibyście więcej tego rodzaju contentu na blogu!

O autorze

Adam Bartoszko

Programista Angular interesujący się wytwarzaniem oprogramowania również od strony zarządzania. Wieloletni gracz serii FIFA, którego głównym środkiem transportu są rolki.

Chcesz razem z nami tworzyć treści na bloga? Dołącz do nas i twórz wartościowe treści dla sympatyków Angulara z Angular.love!

3 komentarzy

  1. Avatar Konrad

    Część 🙂 chciałbym się dopytać odnośnie tego plucka, z racji że mamy payload w obiekcie akcji to zamiast konwertować streama kolejnym operatorem nie lepiej będzie zastosować po prostu destrukturyzację? Np. switchMap ({ bookId }) => …

    • Adam Bartoszko Adam Bartoszko

      Cześć, dzięki za komentarz! Masz rację, oczywiście możemy użyć destrukturyzacji. W przypadku podanym w artykule byłoby to krótsze rozwiązanie 🙂
      Ja w swoich projektach z reguły tworzę dedykowane payloady na akcje, np. GetBookPayload { bookId: string }, przez co przy użyciu pluck(‚payload’) ewentualne rozszerzenie payloadu o kolejnego propsa nie wymaga ingerencji w sam effect, o ile ten effect ma na celu tylko zawołanie do data-service i przekazanie zwrotki z api do akcji na success.
      W tym wpisie chciałem wspomnieć o pluck’u, ponieważ z mojego doświadczenia jest to rzadko spotykany i często nieznany przez developerów operator.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *