{"id":30125,"date":"2022-10-06T15:26:06","date_gmt":"2022-10-06T13:26:06","guid":{"rendered":"https:\/\/nearshore-it.eu\/artykuly\/ngrx\/"},"modified":"2024-09-30T05:55:57","modified_gmt":"2024-09-30T03:55:57","slug":"ngrx","status":"publish","type":"post","link":"https:\/\/nearshore-it.eu\/pl\/artykuly\/ngrx\/","title":{"rendered":"Zarz\u0105dzanie stanem aplikacji frontendowej za pomoc\u0105 NgRx"},"content":{"rendered":"\n<div class=\"table-of-contents\">\n    <p class=\"title\">SPIS TRE\u015aCI<\/p>\n    <ol>\n                    <li><a href=\"#Czy-potrzebuj\u0119-NgRx-do-kontroli-stanu-aplikacji?\">1.  Czy potrzebuj\u0119 NgRx do kontroli stanu aplikacji?<\/a><\/li>\n                    <li><a href=\"#Aplikacje-webowe-kiedy\u015b\">2.  Aplikacje webowe kiedy\u015b<\/a><\/li>\n                    <li><a href=\"#Aplikacje-webowe-dzi\u015b\">3.  Aplikacje webowe dzi\u015b<\/a><\/li>\n                    <li><a href=\"#Mo\u017cliwo\u015bci-i-wyzwania-wsp\u00f3\u0142czesnych-aplikacji-webowych\">4.  Mo\u017cliwo\u015bci i wyzwania wsp\u00f3\u0142czesnych aplikacji webowych<\/a><\/li>\n                    <li><a href=\"#Kiedy-zdecydowa\u0107-si\u0119-na-rozwi\u0105zania-do-zarz\u0105dzania-stanem-aplikacji?\">5.  Kiedy zdecydowa\u0107 si\u0119 na rozwi\u0105zania do zarz\u0105dzania stanem aplikacji?<\/a><\/li>\n                    <li><a href=\"#Czym-jest-NgRx?\">6.  Czym jest NgRx?<\/a><\/li>\n                    <li><a href=\"#Kiedy stosowa\u0107 NgRx?\">7.  Kiedy stosowa\u0107 NgRx?<\/a><\/li>\n                    <li><a href=\"#Co-warto-wiedzie\u0107,-zanim-zdecydujemy-si\u0119-na-NgRx?\">8.  Co warto wiedzie\u0107, zanim zdecydujemy si\u0119 na NgRx?<\/a><\/li>\n                    <li><a href=\"#Architektura Redux\">9.  Architektura Redux<\/a><\/li>\n                    <li><a href=\"#Alternatywa dla NgRx\">10.  Alternatywa dla NgRx<\/a><\/li>\n                    <li><a href=\"#Podsumowanie\">11.  Podsumowanie<\/a><\/li>\n            <\/ol>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"Czy-potrzebuj\u0119-NgRx-do-kontroli-stanu-aplikacji?\">Czy potrzebuj\u0119 NgRx do kontroli stanu aplikacji?<\/h2>\n\n\n\n<p>Obecne aplikacje webowe maj\u0105 coraz mniej wsp\u00f3lnego z aplikacjami, kt\u00f3re widzieli\u015bmy jeszcze par\u0119na\u015bcie lat temu. Szybko\u015b\u0107 rozwoju wszelkiego rodzaju bibliotek, framework\u00f3w, jak i samego podej\u015bcia do pisania frontendu aplikacji uleg\u0142y znacznej zmianie.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"Aplikacje-webowe-kiedy\u015b\">Aplikacje webowe kiedy\u015b<\/h2>\n\n\n\n<p>Dawniej strony internetowe tworzono w formie statycznej, gdzie tre\u015b\u0107 strony nie by\u0142a generowana po stronie serwera czy backendu. By\u0142y one statyczne i rzadko aktualizowane. Rozwi\u0105zanie to by\u0142o bardzo szybkie w implementacji, ale i bardzo ma\u0142o elastyczne, nie nadawa\u0142o si\u0119 ono r\u00f3wnie\u017c do tworzenia dynamicznych aplikacji.<\/p>\n\n\n\n<p>Oczywi\u015bcie mo\u017cna by\u0142o dodawa\u0107 r\u00f3\u017cne skrypty do takiej strony internetowej, aby stworzy\u0107 iluzj\u0119 dynamiki, ale nie by\u0142o to rozwi\u0105zanie rozbudowane w takim stopniu, w jakim widzimy to dzisiaj.<\/p>\n\n\n\n<p>Z biegiem czasu zacz\u0119to wi\u0119c stosowa\u0107 rozwi\u0105zanie <strong>o<\/strong> <strong>nazwie MPA (Multi Page Appliaction).<\/strong> W wielkim skr\u00f3cie chodzi o to, \u017ce po stronie backendu generowa\u0142a si\u0119 dynamiczna tre\u015b\u0107 HTML, a niekt\u00f3re interakcje u\u017cytkownika, takie jak przej\u015bcie na kolejn\u0105 podstron\u0119, powodowa\u0142y kolejne wys\u0142anie zapytania do serwera z pro\u015bb\u0105 o wygenerowanie nowego widoku i jego wy\u015bwietlenie po stronie przegl\u0105darki.<\/p>\n\n\n\n<p>Rozwi\u0105zanie takie powodowa\u0142o wiele zapyta\u0144 do serwera, co by\u0142o dosy\u0107 uci\u0105\u017cliwe w tamtych czasach, jednak nawet dzisiaj spora cz\u0119\u015b\u0107 portali e-commerce, blog\u00f3w oraz for\u00f3w internetowych dzia\u0142a dzi\u0119ki temu w\u0142a\u015bnie rozwi\u0105zaniu.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"Aplikacje-webowe-dzi\u015b\">Aplikacje webowe dzi\u015b<\/h2>\n\n\n\n<p>Potrzeby u\u017cytkownik\u00f3w, jak i trendy si\u0119 zmieniaj\u0105. Na chwil\u0119 obecn\u0105 spora cz\u0119\u015b\u0107 aplikacji jest tworzona w technologii <strong>SPA (Single Page Application).<\/strong> Zalet\u0105 stosowania tego rozwi\u0105zania jest przede wszystkim to, \u017ce aplikacja nie prze\u0142adowuje si\u0119 za ka\u017cdym razem, kiedy u\u017cytkownik wejdzie z ni\u0105 w interakcj\u0119.<\/p>\n\n\n\n<p>Jest to rozwi\u0105zanie bardziej efektywne, du\u017co szybsze i na pewno mniej obci\u0105\u017caj\u0105ce dla serwera. Od momentu pojawienia si\u0119 SPA zacz\u0119\u0142y si\u0119 pojawia\u0107 coraz bardziej rozbudowane i skomplikowane aplikacje internetowe, poniewa\u017c ca\u0142a tre\u015b\u0107 mog\u0142a by\u0107 generowana ju\u017c po stronie frontendu ca\u0142kowicie dynamicznie, a dane z serwera by\u0142y pobierane bez prze\u0142adowania za pomoc\u0105 np. REST API.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"Mo\u017cliwo\u015bci-i-wyzwania-wsp\u00f3\u0142czesnych-aplikacji-webowych\">Mo\u017cliwo\u015bci i wyzwania wsp\u00f3\u0142czesnych aplikacji webowych<\/h2>\n\n\n\n<p>Kiedy aplikacje internetowe zacz\u0119\u0142y coraz bardziej przypomina\u0107 aplikacje, kt\u00f3re instalujemy na komputerze czy telefonie, pojawi\u0142y si\u0119 te\u017c pewne nowe mo\u017cliwo\u015bci, ale i przeszkody.<\/p>\n\n\n\n<p>Z uwagi na to, \u017ce to ju\u017c nie serwer by\u0142 odpowiedzialny za generowanie plik\u00f3w HTML z now\u0105 tre\u015bci\u0105, a sama przegl\u0105darka z u\u017cyciem JavaScript generowa\u0142a dynamicznie tre\u015b\u0107, w pewnym momencie danych by\u0142o tak wiele, \u017ce trzeba by\u0142o je gdzie\u015b przechowywa\u0107.<\/p>\n\n\n\n<p>O ile jeszcze przechowywanie stanu w mniejszych, a nawet \u015brednich aplikacjach nie jest jakim\u015b du\u017cym problemem, o tyle w aplikacjach du\u017co wi\u0119kszych, nawet gdy mamy bardzo dobr\u0105 architektur\u0119, w pewnym momencie jest nie lada wyzwaniem. <strong>Cz\u0119sto bez dobrego rozwi\u0105zania do przechowywania stanu mo\u017cemy sko\u0144czy\u0107 na refaktorze sporej cz\u0119\u015bci kodu<\/strong>.<\/p>\n\n\n<div class=\"special-content-box style-1\">\r\n    <div class=\"box\">\r\n                <div class=\"content\">\r\n                                <\/div>\r\n    <\/div>\r\n<\/div>\r\n\n\n\n<h3 class=\"wp-block-heading\">Ma\u0142e aplikacje<\/h3>\n\n\n\n<p>Dla tych najmniejszych aplikacji prawdopodobnie mo\u017cemy pokusi\u0107 si\u0119 o u\u017cycie mniejszej biblioteki lub nawet samego JavaScriptu do przechowywania stanu, bo nie ma tam przewa\u017cnie za du\u017co oblicze\u0144, interakcji czy te\u017c zapyta\u0144 do API. Z tego wzgl\u0119du czasami nie ma sensu zaprz\u0119ga\u0107 wi\u0119kszego frameworka, biblioteki czy te\u017c innego narz\u0119dzia do przechowywania aktualnego stanu aplikacji.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Bardziej rozbudowane aplikacje<\/h3>\n\n\n\n<p>Przy aplikacjach wi\u0119kszych, tak jak wspomnia\u0142em wcze\u015bniej, oczywi\u015bcie stan aplikacji da si\u0119 przechowywa\u0107 jeszcze w r\u00f3\u017cnego rodzaju dedykowanych serwisach po stronie frontendu, ale z biegiem czasu powi\u0105za\u0144 w kodzie jest coraz wi\u0119cej, staje si\u0119 on coraz bardziej skomplikowany, a co za tym idzie \u2013 <strong>po prostu trudny w utrzymaniu.<\/strong> Czasami \u2013 nawet nieprzewidywalny w swoim dzia\u0142aniu.<\/p>\n\n\n\n<p>W momencie, kiedy aplikacja osi\u0105gn\u0119\u0142a spore rozmiary, mo\u017ce doj\u015b\u0107 do sytuacji, w kt\u00f3rej maj\u0105c dany stan aplikacji w kilku miejscach, tak naprawd\u0119 nie mamy jednego \u017ar\u00f3d\u0142a prawdy, czyli miejsca, gdzie dla danego zapytania otrzymamy zawsze ten sam wynik, a nie dwa ca\u0142kowicie inne.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"Kiedy-zdecydowa\u0107-si\u0119-na-rozwi\u0105zania-do-zarz\u0105dzania-stanem-aplikacji?\">Kiedy zdecydowa\u0107 si\u0119 na rozwi\u0105zania do zarz\u0105dzania stanem aplikacji?<\/h2>\n\n\n\n<p>Wyobra\u017amy sobie sytuacj\u0119, w kt\u00f3rej aplikacja sklepu internetowego w przegl\u0105darce renderuje nam dwie r\u00f3\u017cne ceny takiego samego produktu, chocia\u017c powinny one by\u0107 identyczne. Albo sytuacj\u0119, gdy u\u017cytkownik zosta\u0142 wylogowany, a mimo to na stronie wy\u015bwietlaj\u0105 mu si\u0119 jeszcze informacje u\u017cytkownika.<\/p>\n\n\n\n<p>Wbrew pozorom, kiedy nie polegamy na jednym \u017ar\u00f3dle prawdy, takich sytuacji jest wiele, i kiedy do tego dochodzi, warto zastanowi\u0107 si\u0119: <strong>mo\u017ce ju\u017c czas, by zastosowa\u0107 biblioteki do zarz\u0105dzania stanem aplikacji?<\/strong><\/p>\n\n\n\n<p><strong>Taka decyzja powinna zapa\u015b\u0107 mo\u017cliwie jak najwcze\u015bniej <\/strong>podczas tworzenia aplikacji internetowej, bo przewa\u017cnie na p\u00f3\u017aniejszym etapie jest to cz\u0119sto niemo\u017cliwe albo wi\u0105\u017ce si\u0119 z przerabianiem sporej cz\u0119\u015bci kodu i dostosowywaniem starej cz\u0119\u015bci aplikacji, <strong>co jest niestety bardzo kosztowne.<\/strong><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"Czym-jest-NgRx?\">Czym jest NgRx?<\/h2>\n\n\n\n<p>Czym w takim razie jest NgRx i jak nam mo\u017ce pom\u00f3c?<\/p>\n\n\n\n<p><strong>NgRx jest to zbi\u00f3r bibliotek, kt\u00f3re s\u0142u\u017c\u0105 do budowania reaktywnych aplikacji z wykorzystaniem Angulara.<\/strong> Dzi\u0119ki niemu mo\u017cemy zarz\u0105dza\u0107 stanem z wykorzystaniem mi\u0119dzy innymi wyizolowanych efekt\u00f3w ubocznych. Mamy jedno \u017ar\u00f3d\u0142o prawdy, a wi\u0119c ju\u017c nigdy nie b\u0119dziemy musieli zastanawia\u0107 si\u0119 czy np. tak jak w przyk\u0142adzie powy\u017cej \u2013 cena danego produktu jest prawid\u0142owa.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Kiedy stosowa\u0107 NgRx?<\/h2>\n\n\n\n<p>NgRx do kontroli stanu aplikacji nadaje si\u0119 idealnie w przypadkach, kiedy w warstwie UI sporo si\u0119 dzieje. Przyk\u0142adowo:<\/p>\n\n\n<div class=\"special-content-box style-1\">\r\n    <div class=\"box\">\r\n                <div class=\"content\">\r\n                                <\/div>\r\n    <\/div>\r\n<\/div>\r\n\n\n\n<h2 class=\"wp-block-heading\" id=\"Co-warto-wiedzie\u0107,-zanim-zdecydujemy-si\u0119-na-NgRx?\">Co warto wiedzie\u0107, zanim zdecydujemy si\u0119 na NgRx?<\/h2>\n\n\n\n<p><strong>Nale\u017cy pami\u0119ta\u0107, \u017ce pr\u00f3g wej\u015bcia jest dosy\u0107 wysoki,<\/strong> poniewa\u017c opr\u00f3cz samej znajomo\u015bci biblioteki i jej architektury trzeba bardzo \u015bci\u015ble przestrzega\u0107 pewnych konwencji pisania kodu, zna\u0107 RxJS na dosy\u0107 wysokim poziomie. W ostatecznym rozrachunku jednak korzy\u015bci ze stosowania NgRx przewa\u017c\u0105. Nak\u0142ad pracy te\u017c nie jest bez znaczenia, bo o ile przy pisaniu zwyk\u0142ych, niezb\u0119dnych dla aplikacji serwis\u00f3w napiszemy kod bardzo szybko, o tyle przy u\u017cywaniu NgRx b\u0119dzie to trwa\u0142o kilka razy d\u0142u\u017cej z uwagi na jego architektur\u0119. A przecie\u017c dodatkowo musimy napisa\u0107 jeszcze testy jednostkowe.<\/p>\n\n\n\n<p>Wydaje si\u0119 to dosy\u0107 skomplikowane na pocz\u0105tek, prawda? <strong>Zobaczmy wi\u0119c, jak wygl\u0105da taka architektura od \u015brodka.<\/strong><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Architektura Redux<\/h2>\n\n\n\n<p>Architektura Redux cechuje si\u0119 tym, \u017ce wszystko, co dzieje si\u0119 w aplikacji, jest bardzo przewidywalne. Dzieje si\u0119 tak dlatego, \u017ce mamy tam jednokierunkowy przep\u0142yw danych, kt\u00f3ry wymusza na nas pewne konwencje oraz \u015bcis\u0142e zdefiniowanie, kiedy ma zosta\u0107 zmieniony stan aplikacji. Dzi\u0119ki temu chroni ona aplikacj\u0119 przed niepo\u017c\u0105danymi zmianami stanu.<\/p>\n\n\n\n<p>Jedn\u0105 z g\u0142\u00f3wnych zasad Reduxa jest posiadanie jednego globalnego stanu aplikacji, kt\u00f3ry jest niemutowalny i przechowywany w obiekcie <strong>store, <\/strong>zwanym te\u017c czasami <strong>single source of truth<\/strong> <strong>(SSOT).<\/strong><\/p>\n\n\n\n<p>Zmiany na stanie store mog\u0105 zadzia\u0107 si\u0119 tylko i wy\u0142\u0105cznie za pomoc\u0105 dispatchowania akcji do naszego store i nigdy nie powinni\u015bmy tego robi\u0107 bezpo\u015brednio.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Jak to dzia\u0142a?<\/h3>\n\n\n\n<p>Zaczynaj\u0105c od samej warstwy widoku, podczas interakcji u\u017cytkownika zostaje wywo\u0142ana metoda o nazwie <strong>dispatch,<\/strong> do kt\u00f3rej przekazywany jest w parametrach obiekt akcji. Akcje s\u0105 to cz\u0119sto zwyk\u0142e obiekty, posiadaj\u0105ce sw\u00f3j typ oraz opcjonalnie <strong>payload,<\/strong> czyli dodatkowe informacje, kt\u00f3re mo\u017cemy przekaza\u0107 razem z akcj\u0105.<\/p>\n\n\n\n<p>Z regu\u0142y ka\u017cda akcja, a w\u0142a\u015bciwie jej nazwa powinna by\u0107 unikalna, poniewa\u017c pozwala to dowiedzie\u0107 si\u0119, jaka interakcja w danym momencie zasz\u0142a lub jaki event mia\u0142 miejsce w danym czasie.<\/p>\n\n\n\n<p>Po wywo\u0142aniu metody dispatch z akcj\u0105, store zostaje poinformowany, \u017ce w\u0142a\u015bnie zosta\u0142a zainicjalizowana dana akcja. Nast\u0119pnie trafia ona do reducera wraz z aktualnym stanem aplikacji.<\/p>\n\n\n\n<p>Reducer w tym momencie, na podstawie wys\u0142anej akcji, otrzymuje j\u0105 wraz z aktualnym stanem, a nast\u0119pnie zwraca nam kopi\u0119 nowego stanu. Reducery s\u0105 to czyste funkcje (<strong>pure functions<\/strong>) \u2013 nie powinny mie\u0107 w sobie \u017cadnych efekt\u00f3w ubocznych (<strong>side effects<\/strong>), czyli np. komunikacji z API, a dla danego inputa zwr\u00f3c\u0105 nam zawsze taki sam output.<\/p>\n\n\n\n<p>Po zwr\u00f3ceniu nowej kopii stanu nasz obiekt stanu zostaje zast\u0105piony now\u0105 kopi\u0105, a wszystkie zainteresowane tym obserwatory (<strong>observers<\/strong>) zostaj\u0105 poinformowane o zmianie, co skutkuje np. aktualizacj\u0105 interesuj\u0105cych nas danych po stronie UI.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Inne aspekty w NgRx<\/h3>\n\n\n\n<p>Oczywi\u015bcie opisany wy\u017cej przyk\u0142ad jest typowym flow w NgRx i nie uwzgl\u0119dnia takich aspekt\u00f3w jak zapytania do API, r\u00f3\u017cnego rodzaju loggery czy inne ciekawostki, ale i tutaj mamy wprowadzon\u0105 dla nas warstw\u0119 middleware, z kt\u00f3rej mo\u017cemy skorzysta\u0107 \u2013 s\u0105 to efekty uboczne, czyli side effects.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Efekty uboczne \u2013 side effects<\/h3>\n\n\n\n<p>Side effects w uproszczeniu dzia\u0142aj\u0105 w ten spos\u00f3b, \u017ce za ka\u017cdym razem, kiedy wywo\u0142ujemy metod\u0119 dispatch z dan\u0105 akcj\u0105, powinna ona przej\u015b\u0107 przez wszystkie warstwy middleware razem ze stanem.<\/p>\n\n\n\n<p>Efekt uboczny na podstawie typu akcji decyduje, czy zwr\u00f3ci now\u0105 akcj\u0119 asynchronicznie, czy te\u017c nie.<\/p>\n\n\n\n<p>Jednym z przyk\u0142ad\u00f3w u\u017cycia side effect jest wy\u017cej wspominana komunikacja z API. Do efektu trafia dana akcja, a nastepnie prewa\u017cnie z u\u017cyciem dedykowanego serwisu komunikujemy si\u0119 z naszym REST API, aby nast\u0119pnie zwr\u00f3ci\u0107 akcj\u0119.<\/p>\n\n\n\n<p>Niepisan\u0105 zasad\u0105 jest, \u017ce przewa\u017cnie w tej sytuacji zwracamy akcj\u0119 typu <strong>success <\/strong>lub <strong>failed<\/strong>, w zale\u017cno\u015bci od tego, czy z serwera przysz\u0142a odpowied\u017a prawid\u0142owa, czy te\u017c b\u0142\u0119dna. Na podstawie typu danej akcji mo\u017cemy obs\u0142u\u017cy\u0107 j\u0105 po stronie UI.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2024\/09\/jpro_2022.10.04_graphic_1.png\" alt=\"side effects\" class=\"wp-image-68188\" title=\"\"><\/figure>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Store<\/h3>\n\n\n\n<p>Store, jak wskazuje nazwa, odpowiada za przechowywanie stanu naszej aplikacji. To miejsce, gdzie \u0142\u0105cz\u0105 si\u0119 wszystkie cz\u0119\u015bci architektury Redux.<\/p>\n\n\n\n<p>Sam store mo\u017ce mie\u0107 zadeklarowane pocz\u0105tkowo dane, kt\u00f3re p\u00f3\u017aniej zmieniaj\u0105 si\u0119 wraz z interakcj\u0105 u\u017cytkownika w aplikacji za pomoc\u0105 specjalnych funkcji \u2013 reducer\u00f3w. Obiekt stanu nie musi by\u0107 monolitem; mo\u017cemy posiada\u0107 kilka podstan\u00f3w dla ka\u017cdej interesuj\u0105cej nas funkcjonalno\u015bci lub modu\u0142u aplikacji.<\/p>\n\n\n\n<p>Dla przyk\u0142adu, w store mo\u017cemy przechowywa\u0107 takie informacje jak dane zalogowanego u\u017cytkownika, wybrane produkty w koszyku w sklepie czy nawet osobno wype\u0142nione dane w formularzu do wysy\u0142ki produktu, kt\u00f3ry przed chwil\u0105 wype\u0142niali\u015bmy, ale z jakiego\u015b powodu wr\u00f3cili\u015bmy na poprzedni\u0105 stron\u0119.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Akcja<\/h3>\n\n\n\n<p>Akcje s\u0105 to klasy implementuj\u0105ce interfejs Action. Reprezentuj\u0105 one unikalne eventy, kt\u00f3re wyst\u0119puj\u0105 w aplikacji podczas r\u00f3\u017cnego rodzaju interakcji u\u017cytkownika z UI, jak r\u00f3wnie\u017c takie, kt\u00f3rych nie widzimy np. w efektach. Akcja posiada sw\u00f3j okre\u015blony typ, cz\u0119sto te\u017c posiada payload, czyli dane, kt\u00f3re przekazujemy wraz z akcj\u0105.<\/p>\n\n\n\n<p>Akcje pozwalaj\u0105 nam zrozumie\u0107 ca\u0142y flow w aplikacji, s\u0105 bardzo przewidywalne, i dzi\u0119ki specjalnym narz\u0119dziom do debugowania \u2013 mo\u017cemy szybko prze\u015bledzi\u0107 ich dzia\u0142anie i wychwyci\u0107 ewentualny b\u0142\u0105d w aplikacji.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Reducer<\/h3>\n\n\n\n<p>Reducery to czyste funkcje przyjmuj\u0105ce dwa parametry \u2013 aktualny stan aplikacji oraz akcj\u0119, kt\u00f3r\u0105 przekazali\u015bmy. Funkcje te odpowiadaj\u0105 za stan aplikacji i tylko z ich pomoc\u0105 powinni\u015bmy zmienia\u0107 stan aplikacji, nigdy bezpo\u015brednio.<\/p>\n\n\n\n<p>Reducer sprawdza typ akcji i na podstawie zaimplementowanej logiki w funkcji decyduje, co zrobi\u0107 ze stanem. Dla przyk\u0142adu, kiedy chcemy zaktualizowa\u0107 dane u\u017cytkownika, mo\u017cemy wywo\u0142a\u0107 metod\u0119 dispatch z danym payloadem o przyk\u0142adowej nazwie <strong>UPDATE_USER.<\/strong> W momencie gdy akcja trafi do reducera, sprawdzi on jej typ, a nast\u0119pnie na podstawie typu znajdzie odpowiedni\u0105 cz\u0119\u015b\u0107 kodu do wykonania i zwr\u00f3ci now\u0105 kopi\u0119 stanu wraz z uzupe\u0142nionymi danymi u\u017cytkownika.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Efekt<\/h3>\n\n\n\n<p>Efekty dzi\u0119ki zasileniu przez RxJS daj\u0105 nam mo\u017cliwo\u015b\u0107 tworzenia side effects. Efekty stanowi\u0105 dedykowane miejsca, w kt\u00f3rych mo\u017cemy oddelegowa\u0107 r\u00f3\u017cne czynno\u015bci, takie jak pobieranie danych z REST API, a o kt\u00f3rych tak naprawd\u0119 komponenty nie powinny wiedzie\u0107.<\/p>\n\n\n\n<p>Efekty izoluj\u0105 side effects od komponent\u00f3w, pozwalaj\u0105 na zachowanie w nich bardzo czystego kodu.<\/p>\n\n\n\n<p>Dla przyk\u0142adu, kiedy wywo\u0142amy metod\u0119 dispatch z akcj\u0105 <strong>GET_USER,<\/strong> akcja zostanie wychwycona po swoim typie i ju\u017c wewn\u0105trz efektu nast\u0119puje komunikacja z REST API za pomoc\u0105 serwisu.<\/p>\n\n\n\n<p>Kiedy dane z REST API zostan\u0105 pobrane, efekt zwr\u00f3ci nam now\u0105 akcj\u0119, mo\u017ce to by\u0107 np. akcja typu <strong>GET_USER_SUCCESS<\/strong> lub <strong>GET_USER_FAILED.<\/strong> Nast\u0119pnie na podstawie tych akcji mo\u017cemy np. wy\u015bwietli\u0107 odpowiedni komunikat dla u\u017cytkownika o b\u0142\u0119dzie lub obs\u0142u\u017cy\u0107 j\u0105 w inny dowolny spos\u00f3b po stronie aplikacji.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Selektor<\/h3>\n\n\n\n<p>Selektory to pure functions, kt\u00f3rych u\u017cywamy w momencie, kiedy chcemy pobra\u0107 jakie\u015b dane ze store. <strong>Pure function oznacza, \u017ce zawsze dla tych samych argument\u00f3w zostanie zwr\u00f3cony ten sam wynik.<\/strong><\/p>\n\n\n\n<p>Selektor\u00f3w mo\u017cemy u\u017cywa\u0107 w komponentach, aby nas\u0142uchiwa\u0107 na zmiany w store \u2013 wystarczy je raz zasubskrybowa\u0107. Podczas zmiany stanu obserwator zostanie poinformowany o tym fakcie, a co za tym idzie \u2013 UI zostanie zaktualizowany. To te\u017c oczywi\u015bcie zale\u017cy od naszej implementacji.<\/p>\n\n\n\n<p>Stan aplikacji mo\u017ce by\u0107 jednym wielkim p\u0142askim obiektem, ale mo\u017ce by\u0107 te\u017c podzielony na mniejsze cz\u0119\u015bci, np. takie jak panel u\u017cytkownika. Dla przyk\u0142adu, chc\u0105c pobra\u0107 imi\u0119 i nazwisko danego u\u017cytkownika ze store, wystarczy utworzy\u0107 selektor, kt\u00f3ry pobierze tylko taki wycinek, kt\u00f3ry nas interesuje, a nie ca\u0142o\u015b\u0107 stanu aplikacji.<\/p>\n\n\n\n<p>W praktyce wygl\u0105da to tak, \u017ce w aplikacji mamy bardzo du\u017co selektor\u00f3w. Jedne odpowiadaj\u0105 za pobranie danych produktu, drugie pobieraj\u0105 dane u\u017cytkownika, a jeszcze inne np. list\u0119 produkt\u00f3w w wyszukiwarce.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Alternatywa dla NgRx<\/h2>\n\n\n\n<p>Jak wida\u0107, architektura Redux posiada elementy, kt\u00f3re mo\u017cemy wykorzystywa\u0107 odpowiednio do w\u0142asnych potrzeb, ale nie nale\u017cy ona do najprostszych. Nak\u0142ad pracy do stworzenia dobrze dzia\u0142aj\u0105cej aplikacji r\u00f3wnie\u017c jest niema\u0142y.<\/p>\n\n\n\n<p>Obecnie jest dost\u0119pnych kilka ca\u0142kiem ciekawych alternatyw dla NgRx. Poni\u017cej postaram si\u0119 przedstawi\u0107 kilka z nich.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Akita<\/h3>\n\n\n\n<p>Akita to rozwi\u0105zanie do zarz\u0105dzania stanem, zainspirowane koncepcjami z Fluxa i Reduxa. Cechuj\u0105 j\u0105 prostota oraz szybko\u015b\u0107 implementacji w aplikacji.<\/p>\n\n\n\n<p>Akita ma du\u017co ni\u017cszy pr\u00f3g wej\u015bcia ni\u017c NgRx, wi\u0119c nawet mniej do\u015bwiadczeni developerzy b\u0119d\u0105 sobie w stanie poradzi\u0107. Dodatkowo posiada dosy\u0107 obszerny zestaw narz\u0119dzi, kt\u00f3re przydadz\u0105 si\u0119, aby szybko wystartowa\u0107 z aplikacj\u0105.<\/p>\n\n\n\n<p>Kolejn\u0105 zalet\u0105 Akity jest to, \u017ce nie jest \u015bci\u015ble powi\u0105zana z Angularem, a co za tym idzie \u2013 mo\u017cemy jej u\u017cy\u0107 przy projektach kt\u00f3re korzystaj\u0105 z <strong>Reacta, Vue.js, Svelte czy nawet zwyk\u0142ego JS.<\/strong> Nie potrzebujemy tutaj nic wi\u0119cej \u2013 wystarczy jej u\u017cy\u0107; kod do tego potrzebny jest zredukowany do minimum.<\/p>\n\n\n\n<p>Nale\u017cy te\u017c wspomnie\u0107 o tym, \u017ce Akita posiada bardzo obszern\u0105 dokumentacj\u0119. Autorzy naprawd\u0119 si\u0119 postarali. W dokumentacji znajdziemy wszystkie potrzebne informacje na start, a wielko\u015b\u0107 spo\u0142eczno\u015bci, kt\u00f3ra korzysta z tego rozwi\u0105zania, \u015bwiadczy o tym, \u017ce coraz bardziej zyskuje ono na popularno\u015bci.<\/p>\n\n\n\n<h5 class=\"wp-block-heading\">Architektura Akity<\/h5>\n\n\n\n<p>Je\u015bli chodzi o architektur\u0119 Akity, jest ona podobna do tej, kt\u00f3r\u0105 proponuje NgRx, tylko troch\u0119 uszczuplona o niekt\u00f3re elementy. Kluczowe tutaj jest posiadanie stanu jako jednego obiektu b\u0119d\u0105cego jedynym \u017ar\u00f3d\u0142em prawdy.<\/p>\n\n\n\n<p>Zmiana stanu mo\u017ce zadzia\u0107 si\u0119 tylko i wy\u0142\u0105cznie za pomoc\u0105 wywo\u0142ania metody <strong>setState<\/strong> lub jednej z metod aktualizacji bazuj\u0105cej na tej metodzie. W NgRx posiadali\u015bmy podobn\u0105 metod\u0119 o nazwie <strong>dispatch.<\/strong><\/p>\n\n\n\n<p>Kolejn\u0105 wa\u017cn\u0105 zasad\u0105 jest to, \u017ce komponent nie mo\u017ce pobiera\u0107 danych bezpo\u015brednio ze store. Powinno dzia\u0107 si\u0119 to za pomoc\u0105 zapyta\u0144 (query), czyli bardzo podobnie jak w NgRx, gdzie s\u0105 selektory.<\/p>\n\n\n\n<p>Logika oraz wywo\u0142ania aktualizacji powinny by\u0107 ca\u0142kowicie oddzielone od komponent\u00f3w i zamkni\u0119te w dedykowanych serwisach.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2024\/09\/jpro_2022.10.04_graphic_2.png\" alt=\"Akita\" class=\"wp-image-68192\" title=\"\"><\/figure>\n<\/div>\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">NGXS<\/h3>\n\n\n\n<p>Jest to kolejna alternatywa dla NgRx, pozwalaj\u0105ca na zarz\u0105dzanie stanem w aplikacjach angularowych. NGXS zachowuje zasad\u0119 jedynego \u017ar\u00f3d\u0142a prawdy, jest bardzo \u0142atwy w implementacji, kod potrzebny do jego wdro\u017cenia redukuje do absolutnego minimum.<\/p>\n\n\n\n<p>W por\u00f3wnaniu do NgRx, gdzie musieli\u015bmy tworzy\u0107 r\u00f3\u017cnego rodzaju stany, akcje, reducery i efekty, w tym przypadku to wszystko praktycznie sprowadza si\u0119 do utworzenia obiektu stanu i odpowiednich akcji, a z reszt\u0105 NGXS sobie poradzi.<\/p>\n\n\n\n<p>Tak jak ju\u017c wcze\u015bniej wspomnia\u0142em, jest to rozwi\u0105zanie du\u017co mniej skomplikowane ni\u017c te, kt\u00f3re proponuj\u0105 NgRx czy nawet Akita. Znacznie redukuje kod, co sprawia, \u017ce wszystko jest tak proste, jak to tylko mo\u017cliwe. Co wi\u0119cej, nie musimy by\u0107 te\u017c obeznani z bibliotek\u0105 RxJS, co jest dla mnie osobi\u015bcie du\u017cym plusem. Rekomendowa\u0142bym to rozwi\u0105zanie osobom, kt\u00f3re dopiero zaczynaj\u0105 swoj\u0105 przygod\u0119 z rozwi\u0105zaniami do zarz\u0105dzania stanem.<\/p>\n\n\n\n<h5 class=\"wp-block-heading\">Architektura NGXS<\/h5>\n\n\n\n<p>NGXS bazuje na wzorcu CQRS, kt\u00f3ry znamy z implementacji w bibliotekach takich jak Redux i NgRx. Architektura sk\u0142ada si\u0119 w\u0142a\u015bciwie z czterech g\u0142\u00f3wnych komponent\u00f3w: <strong>store, akcja, stan i selektor.<\/strong> Podobnie jak w NgRx zapewniaj\u0105 one jednokierunkowy przep\u0142yw danych z komponentu do store za pomoc\u0105 akcji, a pobieranie danych (podobnie zreszt\u0105 jak w NgRx) jest mo\u017cliwe za pomoc\u0105 selektor\u00f3w.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Store<\/strong> \u2013 w NGXS jest globalnym managerem stanu, kt\u00f3ry oddelegowuje akcje do odpowiednich kontener\u00f3w oraz pozwala nam pobiera\u0107 z nich \u201ewycinki\u201d danych z globalnego stanu aplikacji.<\/li>\n\n\n\n<li><strong>Akcje<\/strong> \u2013 unikalne eventy, kt\u00f3re maj\u0105 miejsce w odpowiedzi na r\u00f3\u017cnego rodzaju interakcje u\u017cytkownika (np. z warstw\u0105 UI) i pozwalaj\u0105 nam si\u0119 komunikowa\u0107 ze store, kt\u00f3ry na ich podstawie wie, co zrobi\u0107.<\/li>\n\n\n\n<li><strong>Stany<\/strong> \u2013 stanem w NGXS mo\u017cna nazwa\u0107 klasy, kt\u00f3re posiadaj\u0105 specjalne dekoratory z metadat\u0105 oraz mapowaniem akcji.<\/li>\n\n\n\n<li><strong>Selektory<\/strong> \u2013 ostatnim elementem architektury s\u0105 oczywi\u015bcie selektory i podobnie jak w NgRx s\u0105 to zwyk\u0142e funkcje, za pomoc\u0105 kt\u00f3rych mo\u017cemy pobra\u0107 odpowiedni \u201ewycinek\u201d danych z naszego globalnego kontenera stanu.<\/li>\n<\/ul>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2024\/09\/jpro_2022.10.04_graphic_3.png\" alt=\"NGXS\" class=\"wp-image-68196\" title=\"\"><\/figure>\n<\/div>\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">RxJS<\/h3>\n\n\n\n<p>Przejd\u017amy zatem do ostatniej alternatywy, kt\u00f3r\u0105 jest RxJS. W por\u00f3wnaniu do innych bibliotek, kt\u00f3re pozwalaj\u0105 zarz\u0105dza\u0107 stanem za pomoc\u0105 akcji, reducer\u00f3w oraz nawet efekt\u00f3w, RxJS sam w sobie nie daje nam takich mo\u017cliwo\u015bci.<\/p>\n\n\n\n<p>Biblioteka RxJS zosta\u0142a stworzona do obs\u0142ugi asynchronicznych event\u00f3w, za pomoc\u0105 wzorca <strong>observable,<\/strong> kt\u00f3ry daje nam wi\u0119ksze mo\u017cliwo\u015bci i wy\u017cszy poziom abstrakcji, je\u015bli chodzi o ich obs\u0142ug\u0119. NgRx, Akita, NGXS rozwi\u0105zuj\u0105 zupe\u0142nie inne problemy ni\u017c sam RxJS. I chocia\u017c RxJS stworzony by\u0142 zupe\u0142nie do czego\u015b innego, to te biblioteki z niego korzystaj\u0105. W bardzo ma\u0142ych aplikacjach mo\u017cna oczywi\u015bcie u\u017cy\u0107 RxJS nawet bez potrzeby u\u017cywania bibliotek odpowiadaj\u0105cych za stan aplikacji, ale jest to wyzwanie trudne i na pewno nie dla os\u00f3b pocz\u0105tkuj\u0105cych.<\/p>\n\n\n\n<p>Oczywi\u015bcie, zna\u0107 RxJS warto, poniewa\u017c jest on u\u017cywany w wi\u0119kszo\u015bci z wymienionych bibliotek, ale na pocz\u0105tek przygody z zarz\u0105dzaniem stanem aplikacji rozs\u0105dniej b\u0119dzie wybra\u0107 inne rozwi\u0105zanie. Nale\u017cy tutaj pami\u0119ta\u0107, \u017ce w innych rozwi\u0105zaniach mamy dost\u0119pne przer\u00f3\u017cne dodatkowe narz\u0119dzia, akcje, selektory, side effects, narz\u0119dzia developerskie, a w tym przypadku musimy poradzi\u0107 sobie sami, co jest naprawd\u0119 nie lada wyzwaniem.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Podsumowanie<\/h2>\n\n\n\n<p>Jak wida\u0107, jednym z kluczowych wyzwa\u0144, kt\u00f3re napotkamy przy tworzeniu aplikacji internetowych, jest szybkie gromadzenie danych i stabilno\u015b\u0107 dzia\u0142ania. W dzisiejszych czasach sporo os\u00f3b stawia na rozwi\u0105zanie bazuj\u0105ce na Redux, kt\u00f3re umo\u017cliwia modyfikacj\u0119 stanu i pozwala na jednokierunkowy przep\u0142yw danych. Je\u015bli aplikacja jest bardzo dynamiczna, sporo si\u0119 w niej dzieje po stronie frontendu oraz przetwarzamy i wy\u015bwietlamy wiele danych pomi\u0119dzy komponentami, to warto rozwa\u017cy\u0107 wykorzystanie NgRx.<strong> Osobi\u015bcie w takim przypadku polecam to rozwi\u0105zanie, poniewa\u017c pozwala unikn\u0105\u0107 r\u00f3\u017cnych charakterystycznych problem\u00f3w, a zyskamy gotowe i sprawdzone narz\u0119dzia, z kt\u00f3rych korzysta si\u0119 w wielu projektach.<\/strong><\/p>\n\n\n\n<p>Mam nadziej\u0119, \u017ce przygotowane przeze mnie zestawienie zach\u0119ci was do zag\u0142\u0119bienia si\u0119 w temat zarz\u0105dzania stanem, a mo\u017ce nawet spr\u00f3bowania zaimplementowania jednego z tych rozwi\u0105za\u0144 w budowanej aplikacji. Licz\u0119, \u017ce ten artyku\u0142 u\u0142atwi wyb\u00f3r rozwi\u0105zania, kt\u00f3re b\u0119dziecie w stanie sami zaimplementowa\u0107, posi\u0142kuj\u0105c si\u0119 wiedz\u0105, kt\u00f3r\u0105 posiadacie.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Budowane dzi\u015b aplikacje webowe s\u0105 coraz bardziej rozbudowane i dynamiczne oraz przetwarzaj\u0105 sporo danych. W takiej sytuacji wyzwaniem staje si\u0119 zarz\u0105dzanie stanem aplikacji. Sprawd\u017a, kiedy warto zdecydowa\u0107 si\u0119 na rozwi\u0105zanie do kontroli stanu aplikacji i kt\u00f3re wybra\u0107. W artykule omawiam mo\u017cliwo\u015bci NgRx oraz zasad\u0119 dzia\u0142ania architektury Redux. Przedstawiam te\u017c alternatywy, takie jak Akita, NGXS oraz RxJS. Zapraszam do lektury!<\/p>\n","protected":false},"author":89,"featured_media":30136,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"iawp_total_views":144,"footnotes":""},"categories":[1,582],"tags":[],"offering":[513],"class_list":["post-30125","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-artykuly","category-technologie","offering-application-development"],"acf":[],"_links":{"self":[{"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/posts\/30125","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/users\/89"}],"replies":[{"embeddable":true,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/comments?post=30125"}],"version-history":[{"count":2,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/posts\/30125\/revisions"}],"predecessor-version":[{"id":32915,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/posts\/30125\/revisions\/32915"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/media\/30136"}],"wp:attachment":[{"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/media?parent=30125"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/categories?post=30125"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/tags?post=30125"},{"taxonomy":"offering","embeddable":true,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/offering?post=30125"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}