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

Angular Developer w House of Angular. 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.

Zapisz się do naszego newslettera. Bądź na bieżąco z najnowszymi trendami, poradami, meetupami i stań się częścią społeczności Angulara w Polsce. Rynek pracy docenia członków społeczności.

9 komentarzy

  1. 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 }) => …

    • 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.

  2. Wojtas

    Witam, świetny art.
    Mam pytanie ponieważ nigdzie nie mogę znaleźć informacji na ten temat, a śledzę Waszego bloga i widzę, że obracacie się nieźle w temacie. Korzystam z Waszych rad 😉

    W module mam podzielone tematycznie reducery, a struktura wygląda tak, że posiadam główny state (powiedzmy MainState), na którym używam ActionReducerMap, aby zagnieździć tematyczne states. Jeśli z poziomu komponentu odwołuje się do selektora w danym tematycznym state to czy jest różnica gdy do constructora przekażę MainState czy ten tematyczny state (czyli zagnieżdżony)? Czy mogę posługiwać się MainState i nie ma to żadnego znaczenia?

    Pozdrawiam serdecznie!

    • Cześć, dzięki za pozytywny feedback 🙂
      Co do Twojego pytania, to z punktu Angulara nie ma różnicy jak zatypujesz store w konstruktorze, jednak w celach większej czytelności kodu zalecam typowanie konkretnego, tematycznego state’u (o ile oczywiście wykorzystujesz selectory tylko z niego, a nie korzystasz dodatkowo z pozostałych, zagnieżdżonych state’ów), żeby z góry było jasne jaka część stanu jest wykorzystywana w komponencie.

      • Wojtas

        Tak też czułem. Oprócz tego widzę taką różnicę, że mając 2 selektory o tych samych nazwach, korzystając z głównego state w konstruktorze, można się pomylić w imporcie.

        Wielkie dzięki !
        Pozdrawiam

  3. Michał

    Pluck jest spoko, ale trzeba pamiętać, że jest również bugo-genny, jeżeli przyjdzie refactorować obiekt przesyłany przez akcje, to automatyczny refactor większości IDE nie ogarnie plucka, i nie zmieni tam wartości 😛

  4. Pingback: NgRx – tips & tricks - Angular.love

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *