{"id":21247,"date":"2023-04-28T05:00:00","date_gmt":"2023-04-28T03:00:00","guid":{"rendered":"https:\/\/sii.pl\/blog\/?p=21247"},"modified":"2024-07-22T15:01:16","modified_gmt":"2024-07-22T13:01:16","slug":"zarzadzanie-stanem-w-react","status":"publish","type":"post","link":"https:\/\/sii.pl\/blog\/zarzadzanie-stanem-w-react\/","title":{"rendered":"Zarz\u0105dzanie stanem w React"},"content":{"rendered":"\n<p>Rosn\u0105ca popularno\u015b\u0107 architektury Flux oraz implementuj\u0105cego j\u0105 Reduxa sprawi\u0142a, \u017ce praktycznie w ka\u017cdym projekcie opartym na React mo\u017cemy spotka\u0107 wspomnian\u0105 bibliotek\u0119. Czy jest to s\u0142uszne podej\u015bcie? Zdania s\u0105 podzielone. Aby wyrobi\u0107 sobie opini\u0119, zach\u0119cam do lektury artyku\u0142u.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Stan komponentu<\/strong><\/h2>\n\n\n\n<p>Zanim przejdziemy do oceny, wr\u00f3\u0107my do korzeni Reacta. Fundamentami ka\u017cdego komponentu s\u0105 w\u0142a\u015bciwo\u015bci (<strong>props<\/strong>) oraz stan (<strong>state<\/strong>). To w\u0142a\u015bnie stan odpowiada g\u0142\u00f3wnie za przechowywanie danych, kt\u00f3re nast\u0119pnie chcemy u\u017cy\u0107 w naszej aplikacji. To on powoduje ponowne wyrenderowanie komponentu w przypadku jakiejkolwiek zmiany.<\/p>\n\n\n\n<p>React od wersji 16.8 pozwala nam korzysta\u0107 z tzw. <strong>hook\u00f3w<\/strong>. Dzi\u0119ki temu mo\u017cemy w pe\u0142ni zrezygnowa\u0107 z komponent\u00f3w klasowych, a skupi\u0107 si\u0119 na komponentach funkcyjnych. Dla przypomnienia, ka\u017cdy z hook\u00f3w musi zaczyna\u0107 si\u0119 od s\u0142owa \u2018<em>use..\u2019<\/em> i mo\u017ce by\u0107 u\u017cyty jedynie w komponencie lub w innym hooku.<\/p>\n\n\n\n<p>Jednym z najcz\u0119\u015bciej u\u017cywanych hook\u00f3w jest <em>useState<\/em>, odpowiadaj\u0105cy za zarz\u0105dzanie stanem komponentu. Jego sk\u0142adnia wygl\u0105da nast\u0119puj\u0105co:<\/p>\n\n\n\n<p><em>const [stan, funkcja_zmieniaj\u0105ca_stan] = useState(warto\u015b\u0107_pocz\u0105tkowa)<\/em>.<\/p>\n\n\n\n<p>We\u017amy na przyk\u0142ad prosty dialog. Przy pierwszym wyrenderowaniu aplikacji nie chcemy, aby by\u0142 on wy\u015bwietlany. Nast\u0119pnie, po klikni\u0119ciu na przycisk, dialog powinien si\u0119 otworzy\u0107. Mo\u017cemy to zapisa\u0107 w nast\u0119puj\u0105cy spos\u00f3b:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/1.-Prosty-dialog-zarzadzany-stanem.png\"><img decoding=\"async\" width=\"615\" height=\"498\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/1.-Prosty-dialog-zarzadzany-stanem.png\" alt=\"kod\" class=\"wp-image-21286\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/1.-Prosty-dialog-zarzadzany-stanem.png 615w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/1.-Prosty-dialog-zarzadzany-stanem-300x243.png 300w\" sizes=\"(max-width: 615px) 100vw, 615px\" \/><\/a><\/figure><\/div>\n\n\n\n<p>Jak mo\u017cemy zauwa\u017cy\u0107, implementacja jest bardzo prosta i przejrzysta. Ka\u017cda zmiana stanu odpowiednio renderuje komponent i mogliby\u015bmy stwierdzi\u0107, \u017ce \u017cadne dodatkowe narz\u0119dzie nie jest nam potrzebne. Miejmy jednak na uwadze, \u017ce jest to bardzo trywialna aplikacja, wy\u015bwietlaj\u0105ca jedynie statyczne informacje. Co jednak w przypadku, je\u015bli nasz program pobiera\u0142by informacje z backendu, a nast\u0119pnie wy\u015bwietla\u0142 je w kilku r\u00f3\u017cnych miejscach?<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Context API<\/strong><\/h2>\n\n\n\n<p>Z pomoc\u0105 przychodzi nam <strong>kontekst<\/strong>, kt\u00f3rego aktualn\u0105 wersj\u0119 wprowadzono w React 16.3. Jest to mechanizm, kt\u00f3ry upraszcza przekazywanie danych pomi\u0119dzy komponentami, niweluj\u0105c przy tym zjawisko tzw. <strong>\u2018prop-drillingu<\/strong>\u2019. W React informacje przekazywane s\u0105 jednokierunkowo \u2013 od komponent\u00f3w rodzica do komponent\u00f3w dziecka. Przy bardziej rozbudowanych aplikacjach powstaj\u0105ce drzewo zale\u017cno\u015bci potrafi by\u0107 naprawd\u0119 obszerne. Cz\u0119sto okre\u015blon\u0105 informacj\u0119 powinni\u015bmy wy\u015bwietli\u0107 w najni\u017cszym szczeblu drzewa.<\/p>\n\n\n\n<p>Zak\u0142adaj\u0105c, \u017ce dane pobieramy raz, w komponencie rodzica, jeste\u015bmy zmuszeni przekazywa\u0107 <em>propsy<\/em> przez po\u015brednie komponenty, kt\u00f3re nawet z tych danych nie korzystaj\u0105. Pe\u0142ni\u0105 one jedynie rol\u0119 po\u015brednika. Zmniejsza to czytelno\u015b\u0107 kodu, a tak\u017ce powoduje niepotrzebne rendery w przypadku zmian.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/2.-Prop-drilling.png\"><img decoding=\"async\" width=\"957\" height=\"471\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/2.-Prop-drilling.png\" alt=\"Ryc. 1 React \u2013 drzewo zale\u017cno\u015bci\" class=\"wp-image-21289\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/2.-Prop-drilling.png 957w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/2.-Prop-drilling-300x148.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/2.-Prop-drilling-768x378.png 768w\" sizes=\"(max-width: 957px) 100vw, 957px\" \/><\/a><figcaption>Ryc. 1 React \u2013 drzewo zale\u017cno\u015bci<\/figcaption><\/figure><\/div>\n\n\n\n<p>Opisane zjawisko \u2018<strong>prop-drillingu<\/strong>\u2019 jest eliminowane dzi\u0119ki u\u017cyciu kontekstu. Mechanizm ten jest bardzo prosty w implementacji. Wymaga wykonania 3 krok\u00f3w:<\/p>\n\n\n\n<ol class=\"wp-block-list\" type=\"1\"><li>Stworzenie kontekstu, korzystaj\u0105c z funkcji <em>createContext<\/em>, do kt\u00f3rej przekazujemy domy\u015bln\u0105 warto\u015b\u0107.<\/li><li>Stworzenie wrappera, kt\u00f3ry opakowuje interesuj\u0105ce nas komponenty, aby wszystkie z nich mog\u0142y zasubskrybowa\u0107 si\u0119 do zmian kontekstu. Do tego celu powinni\u015bmy u\u017cy\u0107 komponentu <em>Providera<\/em>, istniej\u0105cego w obiekcie naszego kontekstu. Tam przekazujemy aktualn\u0105 warto\u015b\u0107, kt\u00f3r\u0105 powinien przechowa\u0107 kontekst.<\/li><li>U\u017cycie wrappera nad komponentami, kt\u00f3re powinny mie\u0107 dost\u0119p do kontekstu (zazwyczaj na szczycie drzewa naszej aplikacji).<\/li><\/ol>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/3.-Stworzenie-kontekstu.png\"><img decoding=\"async\" width=\"446\" height=\"362\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/3.-Stworzenie-kontekstu.png\" alt=\"kod\" class=\"wp-image-21294\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/3.-Stworzenie-kontekstu.png 446w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/3.-Stworzenie-kontekstu-300x243.png 300w\" sizes=\"(max-width: 446px) 100vw, 446px\" \/><\/a><\/figure><\/div>\n\n\n\n<p><\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/4.-Uzycie-Providera.png\"><img decoding=\"async\" width=\"808\" height=\"523\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/4.-Uzycie-Providera.png\" alt=\"kod\" class=\"wp-image-21297\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/4.-Uzycie-Providera.png 808w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/4.-Uzycie-Providera-300x194.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/4.-Uzycie-Providera-768x497.png 768w\" sizes=\"(max-width: 808px) 100vw, 808px\" \/><\/a><\/figure><\/div>\n\n\n\n<p><\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/5.-Opakowanie-aplikacji-kontekstem.png\"><img decoding=\"async\" width=\"536\" height=\"158\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/5.-Opakowanie-aplikacji-kontekstem.png\" alt=\"kod\" class=\"wp-image-21299\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/5.-Opakowanie-aplikacji-kontekstem.png 536w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/5.-Opakowanie-aplikacji-kontekstem-300x88.png 300w\" sizes=\"(max-width: 536px) 100vw, 536px\" \/><\/a><\/figure><\/div>\n\n\n\n<p><\/p>\n\n\n\n<p>W tak skonfigurowanej aplikacji jeste\u015bmy w stanie (gra s\u0142\u00f3w niezamierzona) w \u0142atwy spos\u00f3b skorzysta\u0107 ze wsp\u00f3\u0142dzielonych danych na dowolnym poziomie zagnie\u017cd\u017cenia komponentu. Do tego celu mo\u017cemy u\u017cy\u0107 kolejnego z hook\u00f3w, jaki daje nam do dyspozycji React \u2013 <em>useContext<\/em>. Jedyne, co musimy przekaza\u0107, to nazwa kontekstu, z kt\u00f3rego chcemy skorzysta\u0107 (w aplikacji mo\u017cemy mie\u0107 ich wiele). Nast\u0119pnie mo\u017cemy odczyta\u0107 interesuj\u0105c\u0105 nas warto\u015b\u0107, kt\u00f3r\u0105 planujemy wy\u015bwietli\u0107:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/6.-Wykorzystanie-danych-z-kontekstu.png\"><img decoding=\"async\" width=\"646\" height=\"480\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/6.-Wykorzystanie-danych-z-kontekstu.png\" alt=\"kod\" class=\"wp-image-21302\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/6.-Wykorzystanie-danych-z-kontekstu.png 646w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/6.-Wykorzystanie-danych-z-kontekstu-300x223.png 300w\" sizes=\"(max-width: 646px) 100vw, 646px\" \/><\/a><\/figure><\/div>\n\n\n\n<p>Proste, prawda? Musimy jednak pami\u0119ta\u0107, \u017ce sam <strong>Context API nie jest mechanizmem do zarz\u0105dzania stanem<\/strong>! Jest to zestaw narz\u0119dzi, kt\u00f3re pomagaj\u0105 przekazywa\u0107 dane wzd\u0142u\u017c drzewa komponent\u00f3w. Jak mo\u017cna zauwa\u017cy\u0107 w poprzednich kawa\u0142kach kodu, do samego zarz\u0105dzania stanem u\u017cyty zosta\u0142 podstawowy hook Reacta \u2013 <em>useState<\/em>. Warto o tym pami\u0119ta\u0107, poniewa\u017c cz\u0119sto kontekst jest mylnie nazywany narz\u0119dziem do zarz\u0105dzania stanem aplikacji. Tymczasem niczym on nie zarz\u0105dza, jedynie pozwala nam dosta\u0107 si\u0119 do wymaganych danych, bez konieczno\u015bci mozolnego przekazywania zale\u017cno\u015bci w d\u00f3\u0142 drzewa komponent\u00f3w.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Redux<\/strong><\/h2>\n\n\n\n<p>Przy coraz bardziej rozbudowanych aplikacjach korzystaj\u0105cych z coraz wi\u0119kszej liczby danych, u\u017cycie kontekstu mo\u017ce by\u0107 problematyczne. Przede wszystkim nie jest on przystosowany do cz\u0119stych zmian. Przyczynia si\u0119 to do spadku wydajno\u015bci, a tak\u017ce szybko\u015bci dzia\u0142ania aplikacji. W przypadku wielu kontekst\u00f3w, jeste\u015bmy r\u00f3wnie\u017c zmuszeni do opakowania aplikacji ka\u017cdym z nich, co zmniejsza czytelno\u015b\u0107 kodu.<\/p>\n\n\n\n<p><strong>Naprzeciw tym problemom wychodzi Redux, pe\u0142noprawna biblioteka zarz\u0105dzania stanem, najcz\u0119\u015bciej stosowana w\u0142a\u015bnie z Reactem<\/strong>. Bazuje ona na architekturze Flux, o kt\u00f3rej <a href=\"https:\/\/sii.pl\/blog\/flux-architektura-zarzadzania-stanem-w-aplikacjach-webowych\/?category=development-na-miekko&amp;tag=flux,przeglad-narzedzi,zalety-i-wady\" target=\"_blank\" aria-label=\" (opens in a new tab)\" rel=\"noreferrer noopener\" class=\"ek-link\">wi\u0119cej mo\u017cecie przeczyta\u0107 w artykule na naszym blogu.<\/a><\/p>\n\n\n\n<p>Opisuj\u0105c za\u0142o\u017cenia Reduxa, mo\u017cna wyodr\u0119bni\u0107 3 g\u0142\u00f3wne zasady:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Globalny stan aplikacji powinien by\u0107 przechowywany w jednym miejscu jako obiekt zwany magazynem (<strong>store<\/strong>).<\/li><li>Istniej\u0105cy stan aplikacji powinien by\u0107 niezmienialny (<strong>immutable)<\/strong>, tylko do odczytu. W przypadku aktualizacji danych, powinni\u015bmy pracowa\u0107 na kopii obiektu, a nast\u0119pnie zmienion\u0105 kopi\u0119 zwraca\u0107 jako aktualny stan. Dzi\u0119ki temu \u0142atwo mo\u017cna \u015bledzi\u0107 zachodz\u0105ce zmiany, a tak\u017ce unikn\u0105\u0107 przypadkowego nadpisania danych. Jedyn\u0105 mo\u017cliwo\u015bci\u0105 zmiany stanu jest wys\u0142anie (<strong>dispatch<\/strong>) odpowiedniej akcji (<strong>action<\/strong>), czyli obiektu opisuj\u0105cego konkretne zdarzenie w aplikacji.<\/li><li>Za aktualizowanie stanu odpowiedzialny jest tzw. <strong>reducer<\/strong>. Jest to \u201eczysta\u201d funkcja (<strong>pure function <\/strong>\u2013 dla tych samych argument\u00f3w&nbsp; zawsze zwraca tak\u0105 sam\u0105 warto\u015b\u0107 oraz nie powoduje skutk\u00f3w ubocznych, np. w postaci modyfikacji zmiennych poza funkcj\u0105), kt\u00f3ra przyjmuje za argumenty aktualny stan oraz akcj\u0119. Na ich podstawie zwraca <strong>kolejny, nowy stan<\/strong>.<\/li><\/ul>\n\n\n\n<p>To, \u017ce stan powinien by\u0107 niezmienialny, a jednak go aktualizujemy, mo\u017ce by\u0107 pocz\u0105tkowo myl\u0105ce. Warto pami\u0119ta\u0107, \u017ceby <strong>nigdy nie modyfikowa\u0107 istniej\u0105cego obiektu stanu<\/strong>, natomiast zawsze pracowa\u0107 na jego kopii.<\/p>\n\n\n\n<p>Przejd\u017amy do stworzonego wcze\u015bniej dialogu i za\u0142\u00f3\u017cmy, \u017ce chcemy doda\u0107 kolejne pole z danymi u\u017cytkownika \u2013 tym razem jego wiek. Warto\u015b\u0107 ta mo\u017ce by\u0107 zmieniana przy pomocy przycisk\u00f3w + oraz -, a tak\u017ce po wpisaniu warto\u015bci do pola tekstowego. Zobaczmy wi\u0119c, jakie kroki musimy wykona\u0107, aby zaimplementowa\u0107 t\u0119 logik\u0119 przy pomocy Reduxa.<\/p>\n\n\n\n<p>Dla cel\u00f3w edukacyjnych u\u017cy\u0142em podej\u015bcia, kt\u00f3re przez tw\u00f3rc\u00f3w uznawane jest za nieaktualne (<em>deprecated<\/em>), ale lepiej pokazuje za\u0142o\u017cenia biblioteki. Pod koniec artyku\u0142u kr\u00f3tko opisz\u0119 nowoczesne standardy. Zacznijmy od tego, \u017ce jako zewn\u0119trzn\u0105 paczk\u0119, Reduxa najpierw musimy zainstalowa\u0107. W przypadku Reacta powinni\u015bmy zainstalowa\u0107 2 biblioteki \u2013 <strong>redux<\/strong>, a tak\u017ce <strong>react-redux<\/strong>. Kolejnym krokiem jest stworzenie naszego magazynu (<strong>store<\/strong>). Do tego celu b\u0119dzie nam potrzebny tak\u017ce <strong>reducer<\/strong>, kt\u00f3ry nale\u017cy przekaza\u0107 przy kreacji magazynu.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/7.-Stworzenie-storea.png\"><img decoding=\"async\" width=\"661\" height=\"199\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/7.-Stworzenie-storea.png\" alt=\"kod\" class=\"wp-image-21304\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/7.-Stworzenie-storea.png 661w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/7.-Stworzenie-storea-300x90.png 300w\" sizes=\"(max-width: 661px) 100vw, 661px\" \/><\/a><\/figure><\/div>\n\n\n\n<p><strong>Reducer <\/strong>przyjmuje 2 argumenty \u2013 aktualny stan oraz akcj\u0119. Je\u015bli s\u0142owo <strong>reducer <\/strong>brzmi znajomo, to dlatego, \u017ce pochodzi ono od metody <em>Array.reduce().<\/em> W ten sam spos\u00f3b przyjmuje za argumenty aktualny stan, a tak\u017ce kolejn\u0105 warto\u015b\u0107, kt\u00f3r\u0105 nale\u017cy obs\u0142u\u017cy\u0107. Mo\u017cna powiedzie\u0107, \u017ce <strong>reducer <\/strong>\u201ezbiera\u201d wszystkie akcje, aby na sam koniec sprowadzi\u0107 je jednej ko\u0144cowej warto\u015bci w postaci stanu. Ka\u017cda <strong>akcja <\/strong>musi mie\u0107 okre\u015blony typ, na podstawie kt\u00f3rego <strong>reducer <\/strong>decyduje, w jaki spos\u00f3b stan powinien by\u0107 zaktualizowany. Akcja mo\u017ce mie\u0107 tak\u017ce jaki\u015b \u0142adunek informacji (<strong>payload<\/strong>).<\/p>\n\n\n\n<p>Jak mo\u017cemy zauwa\u017cy\u0107, w przypadku aktualizacji stanu, zwracana jest jego kopia, nigdy nie modyfikujemy bezpo\u015brednio istniej\u0105cego stanu! Gdy przekazana do reducera akcja nie jest znana, a tym samym \u017cadna warto\u015b\u0107 nie jest aktualizowana, zwr\u00f3cony powinien zosta\u0107 aktualny stan.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/8.-Stworzenie-Reducera.png\"><img decoding=\"async\" width=\"753\" height=\"893\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/8.-Stworzenie-Reducera.png\" alt=\"kod\" class=\"wp-image-21306\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/8.-Stworzenie-Reducera.png 753w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/8.-Stworzenie-Reducera-253x300.png 253w\" sizes=\"(max-width: 753px) 100vw, 753px\" \/><\/a><\/figure><\/div>\n\n\n\n<p>Stworzyli\u015bmy magazyn oraz funkcj\u0119 obs\u0142uguj\u0105c\u0105 przychodz\u0105ce zmiany. Teraz pora u\u017cy\u0107 przygotowanych danych w komponentach. W tym celu powinni\u015bmy opakowa\u0107 nasz\u0105 aplikacj\u0119 przy u\u017cyciu komponentu <strong>Providera<\/strong> (podobnie jak w przypadku kontekstu), tym razem importowanegoz biblioteki <em>react-redux.<\/em> Do komponentu nale\u017cy przekaza\u0107 stworzony <strong>store<\/strong>.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/9.-Opakowanie-aplikacji-Providerem-Reduxa.png\"><img decoding=\"async\" width=\"748\" height=\"336\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/9.-Opakowanie-aplikacji-Providerem-Reduxa.png\" alt=\"kod\" class=\"wp-image-21308\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/9.-Opakowanie-aplikacji-Providerem-Reduxa.png 748w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/9.-Opakowanie-aplikacji-Providerem-Reduxa-300x135.png 300w\" sizes=\"(max-width: 748px) 100vw, 748px\" \/><\/a><\/figure><\/div>\n\n\n\n<p>Dzi\u0119ki temu umo\u017cliwili\u015bmy komponentom naszej aplikacji dost\u0119p do magazynu z globalnymi danymi. Spr\u00f3bujmy je w takim razie wykorzysta\u0107. Do tego celu s\u0142u\u017cy hook <em>useSelector<\/em>, kt\u00f3rego argumentem jest funkcja okre\u015blaj\u0105ca jak\u0105 cz\u0119\u015b\u0107 stanu chcemy wykorzysta\u0107. Nast\u0119pnie mo\u017cemy u\u017cywa\u0107 danej warto\u015bci jak zwyk\u0142ej zmiennej.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/10.-Hook-useSelector.png\"><img decoding=\"async\" width=\"671\" height=\"186\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/10.-Hook-useSelector.png\" alt=\"kod\" class=\"wp-image-21310\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/10.-Hook-useSelector.png 671w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/10.-Hook-useSelector-300x83.png 300w\" sizes=\"(max-width: 671px) 100vw, 671px\" \/><\/a><\/figure><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Wysy\u0142anie akcji<\/strong><\/h2>\n\n\n\n<p>Przejd\u017amy teraz do bardziej interesuj\u0105cej kwestii \u2013 wys\u0142ania akcji w celu aktualizacji stanu. Tutaj powinni\u015bmy skorzysta\u0107 z kolejnego hooka biblioteki <em>react-redux<\/em> \u2013 <em>useDispatch<\/em>. Wywo\u0142anie tego hooka daje nam dost\u0119p do funkcji, dzi\u0119ki kt\u00f3rej mo\u017cemy wysy\u0142a\u0107 <strong>akcje <\/strong>do <strong>reducera<\/strong>. Naciskaj\u0105c na przycisk \u2018<em>+\u2019,<\/em> chcieliby\u015bmy zwi\u0119kszy\u0107 wiek u\u017cytkownika. W przypadku klikni\u0119cia w przycisk \u2018<em>-\u2019 <\/em>wykona\u0107 adekwatn\u0105 operacj\u0119.<\/p>\n\n\n\n<p>Aby umo\u017cliwi\u0107 te zdarzenia, nale\u017cy przypisa\u0107 odpowiednie handlery dla event\u00f3w <em>\u2018onClick\u2019<\/em>. W handlerach u\u017cywamy funkcji <strong>dispatch<\/strong>, do kt\u00f3rej przekazujemy odpowiednie obiekty <strong>akcji<\/strong>. Ka\u017cdy z tych obiekt\u00f3w musi zawiera\u0107 typ, na podstawie kt\u00f3rego <strong>reducer<\/strong> zaktualizuje stan. Typ wys\u0142anej akcji jest taki sam, jak uprzednio u\u017cyty w stworzonym reducerze. Na tej podstawie mo\u017ce on odpowiednio zareagowa\u0107. Obiekt akcji mo\u017ce tak\u017ce zawiera\u0107 jak\u0105\u015b informacj\u0119 w postaci payloadu. W naszym przypadku jest to wpisany przez u\u017cytkownika wiek, kt\u00f3ry przekazywany jest do nowego stanu aplikacji (nawiasem m\u00f3wi\u0105c, warto takie update\u2019y opakowa\u0107 w funkcj\u0119 <strong>debounce<\/strong>, aby unikn\u0105\u0107 niepotrzebnych aktualizacji stanu przy ka\u017cdej interakcji u\u017cytkownika).<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/11.-Wyslanie-akcji-do-reducera.png\"><img decoding=\"async\" width=\"836\" height=\"838\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/11.-Wyslanie-akcji-do-reducera.png\" alt=\"kod\" class=\"wp-image-21312\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/11.-Wyslanie-akcji-do-reducera.png 836w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/11.-Wyslanie-akcji-do-reducera-300x300.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/11.-Wyslanie-akcji-do-reducera-150x150.png 150w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/11.-Wyslanie-akcji-do-reducera-768x770.png 768w\" sizes=\"(max-width: 836px) 100vw, 836px\" \/><\/a><\/figure><\/div>\n\n\n\n<p>Podsumowuj\u0105c, stworzyli\u015bmy magazyn (<strong>store<\/strong>), kt\u00f3ry jest naszym jedynym \u017ar\u00f3d\u0142em prawdy. Do magazynu przekazali\u015bmy <strong>reducera<\/strong>, kt\u00f3ry okre\u015bla, jak stan powinien by\u0107 aktualizowany w czasie (nie bezpo\u015brednio zmieniany!), na podstawie przychodz\u0105cych <strong>akcji<\/strong>. <strong>Akcje<\/strong> opisuj\u0105, co w naszej aplikacji si\u0119 wydarzy\u0142o. Wydarzenia te wysy\u0142ane s\u0105 zazwyczaj na podstawie interakcji u\u017cytkownika z naszym UI (wci\u015bni\u0119cie przycisku, zmiana warto\u015bci pola) przy u\u017cyciu <strong>dispatchera<\/strong>. Aby wykorzysta\u0107 istniej\u0105ce dane przechowywane w magazynie, wystarczy zasubskrybowa\u0107 si\u0119 do zmian przy u\u017cyciu <strong>selektora<\/strong>. Ka\u017cda zasubskrybowana zmiana wp\u0142ywa na ponowne renderowanie UI. Proste, prawda?<\/p>\n\n\n\n<p>Ciekawostk\u0105 jest, \u017ce <em>react-redux<\/em> do przekazywania danych pomi\u0119dzy komponentami u\u017cywa kontekstu. Przekazuje <strong>instancj\u0119 magazynu<\/strong>, a nie aktualn\u0105 warto\u015b\u0107 stanu, co korzystnie wp\u0142ywa na performance.<\/p>\n\n\n\n<p>Niezmienialno\u015b\u0107 stanu aplikacji (tworzenie nowego stanu przy ka\u017cdej akcji) ma tak\u017ce wa\u017cn\u0105 implikacj\u0119 \u2013 dzi\u0119ki niej mo\u017cemy skorzysta\u0107 z takich narz\u0119dzi jak <em>Redux DevTools<\/em>, kt\u00f3re pozwalaj\u0105 nam odtworzy\u0107 histori\u0119 poszczeg\u00f3lnych zmian w czasie. Bardzo przydatna rzecz przy debugowaniu.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Aktualnie zalecane podej\u015bcie do implementacji Reduxa<\/strong><\/h2>\n\n\n\n<p>Na koniec obiecane nowoczesne (aktualnie polecane przez tw\u00f3rc\u00f3w) podej\u015bcie do tematu implementacji Reduxa. U\u0142atwia prac\u0119, poniewa\u017c nie zmusza implementuj\u0105cego do ka\u017cdorazowego tworzenia kopii istniej\u0105cego stanu, a tak\u017ce zapobiega powtarzaj\u0105cym si\u0119 b\u0142\u0119dom. Poprzednie przyk\u0142ady s\u0142u\u017cy\u0142y g\u0142\u00f3wnie pokazaniu jak Redux dzia\u0142a, natomiast to jak powinni\u015bcie go implementowa\u0107 w istniej\u0105cych aplikacjach, opisuje poni\u017csze podej\u015bcie.<\/p>\n\n\n\n<p>Opiera si\u0119 ono na bibliotece <em>@reduxjs\/toolkit<\/em> (nazywanej te\u017c skr\u00f3towo RTK). W tym wypadku tworzymy <strong>store<\/strong> przy u\u017cyciu funkcji <em>configureStore<\/em>, do kt\u00f3rej przekazujemy naszego <strong>reducera<\/strong>. Zalet\u0105 tego jest automatyczna konfiguracja podstawowych narz\u0119dzi do pracy z Reduxem, m.in ustalenie po\u0142\u0105czenia do wspomnianego wcze\u015bniej <em>Redux DevTools<\/em>, a tak\u017ce wbudowane oprogramowania po\u015brednie (<em>middleware<\/em>), takie jak <em>redux-thunk<\/em>.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/12.-Tworzenie-storea-przy-uzyciu-toolkita.png\"><img decoding=\"async\" width=\"906\" height=\"238\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/12.-Tworzenie-storea-przy-uzyciu-toolkita.png\" alt=\"kod\" class=\"wp-image-21314\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/12.-Tworzenie-storea-przy-uzyciu-toolkita.png 906w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/12.-Tworzenie-storea-przy-uzyciu-toolkita-300x79.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/12.-Tworzenie-storea-przy-uzyciu-toolkita-768x202.png 768w\" sizes=\"(max-width: 906px) 100vw, 906px\" \/><\/a><\/figure><\/div>\n\n\n\n<p>Tworzenie samego <strong>reducera<\/strong> tak\u017ce zosta\u0142o uproszczone. Logika oparta jest na podzieleniu wi\u0119kszego stanu na kawa\u0142ki (<strong>slice<\/strong>). Wykorzystana zostaje funkcja <em>createSlice()<\/em>, do kt\u00f3rej przekazane zostaj\u0105 nazwa, stan pocz\u0105tkowy oraz obiekt <em>reducers<\/em>, kt\u00f3ry zawiera funkcje, obs\u0142uguj\u0105ce poszczeg\u00f3lne akcje. Jest to analogia do konstrukcji <em>switch\/case<\/em>, kt\u00f3r\u0105 zaimplementowali\u015bmy wcze\u015bniej. Domy\u015blna warto\u015b\u0107 \u2018starego\u2019 switcha jest ju\u017c za nas automatycznie przekazywana.<\/p>\n\n\n\n<p>Co ciekawe <em>createSlice()<\/em> pozwala nam mutowa\u0107 stan! <strong>Jest to jedynie pozorne mutowanie<\/strong>, gdy\u017c pod spodem dzia\u0142a biblioteka <strong>Immer<\/strong>, kt\u00f3ra odnotowuje wszelkie zmiany, a nast\u0119pnie zwraca za nas nowy, zaktualizowany, lecz tak\u017ce niezmienialny stan. Wykonuje za nas prac\u0119, dzi\u0119ki czemu nie musimy skupia\u0107 si\u0119 na kopiowaniu ka\u017cdej w\u0142a\u015bciwo\u015bci obiektu, szczeg\u00f3lnie tej g\u0142\u0119boko zagnie\u017cd\u017conej. <strong>Kolejnym plusem<\/strong> jest to, \u017ce akcje s\u0105 automatycznie tworzone, na podstawie funkcji przekazanych do obiektu <em>reducers<\/em>. Ze stworzonego \u2018<em>slice\u2019a\u2019<\/em> mo\u017cemy wyeksportowa\u0107 wygenerowany <em>reducer<\/em>, kt\u00f3ry zostanie u\u017cyty przy tworzeniu magazynu (store).<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/13.-CreateSlice-z-biblioteki-toolkit.png\"><img decoding=\"async\" width=\"1022\" height=\"620\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/13.-CreateSlice-z-biblioteki-toolkit.png\" alt=\"kod\" class=\"wp-image-21316\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/13.-CreateSlice-z-biblioteki-toolkit.png 1022w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/13.-CreateSlice-z-biblioteki-toolkit-300x182.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/13.-CreateSlice-z-biblioteki-toolkit-768x466.png 768w\" sizes=\"(max-width: 1022px) 100vw, 1022px\" \/><\/a><\/figure><\/div>\n\n\n\n<p>Wykorzystanie danych przechowywanych w magazynie, a tak\u017ce wysy\u0142anie aktualizacji stanu pozostaje takie samo jak w klasycznym podej\u015bciu \u2013 przy u\u017cyciu hook\u00f3w <em>useSelector<\/em> oraz <em>useDispatch<\/em>. W przypadku tego pierwszego, musimy pami\u0119ta\u0107, aby najpierw sprecyzowa\u0107 reducer, do kt\u00f3rego si\u0119 odwo\u0142ujemy. Jego nazwa musi by\u0107 identyczna, jak ta przekazana do obiektu reducers przy konfiguracji store\u2019a.<\/p>\n\n\n\n<p>Wykorzystanie <em>useDispatch<\/em> zosta\u0142o uproszczone \u2013 pozostaje nam jedynie przekaza\u0107 automatycznie wygenerowan\u0105 akcj\u0119, kt\u00f3r\u0105 importujemy z kawa\u0142ka (<strong>slice<\/strong>) naszego stanu. Akcja ta ma posta\u0107 funkcji, kt\u00f3rej wywo\u0142anie zostaje przekazane do dispatcha. Chc\u0105c przekaza\u0107 \u0142adunek (payload), odpowiedni\u0105 warto\u015b\u0107 przekazujemy do wywo\u0142ania akcji. Dzi\u0119ki temu nie jeste\u015bmy zmuszeni do \u017cmudnego tworzenia obiekt\u00f3w z typem, a czasem payloadem danego zdarzenia w aplikacji.<\/p>\n\n\n\n<p>U\u017cycie biblioteki <em>@reduxjs\/toolkit<\/em> w du\u017cym stopniu upraszcza prac\u0119 z Reduxem, a tak\u017ce niweluje powszechnie pope\u0142niane b\u0142\u0119dy. Mimo wszystko uwa\u017cam, \u017ce <strong>warto zna\u0107 podstawy dzia\u0142ania biblioteki<\/strong>, co tym bardziej pozwala doceni\u0107 zalety nowoczesnych rozwi\u0105za\u0144.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/14.-Wyslanie-automatycznie-stworzonych-akcji.png\"><img decoding=\"async\" width=\"798\" height=\"771\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/14.-Wyslanie-automatycznie-stworzonych-akcji.png\" alt=\"kod\" class=\"wp-image-21318\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/14.-Wyslanie-automatycznie-stworzonych-akcji.png 798w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/14.-Wyslanie-automatycznie-stworzonych-akcji-300x290.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/14.-Wyslanie-automatycznie-stworzonych-akcji-768x742.png 768w\" sizes=\"(max-width: 798px) 100vw, 798px\" \/><\/a><\/figure><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Czy Redux to konieczno\u015b\u0107?<\/strong><\/h2>\n\n\n\n<p>React zapewnia nam wiele mo\u017cliwo\u015bci, je\u015bli chodzi o zarz\u0105dzanie stanem. Tylko od nas zale\u017cy w jaki spos\u00f3b je wykorzystamy. Przede wszystkim sprecyzujmy nasze wymagania, a nast\u0119pnie dopasujmy do tego narz\u0119dzia, nie odwrotnie!<\/p>\n\n\n\n<p>Czy powinni\u015bmy korzysta\u0107 z Reduxa? Oczywi\u015bcie. Czy powinni\u015bmy do\u0142\u0105cza\u0107 go do zale\u017cno\u015bci przy ka\u017cdej tworzonej aplikacji? Niekoniecznie.<\/p>\n\n\n\n<p>Jak pokaza\u0142em w artykule, przy u\u017cyciu wbudowanych funkcjonalno\u015bci Reacta jeste\u015bmy w stanie poradzi\u0107 sobie ze \u015brednio skomplikowanymi problemami, gdzie dane s\u0105 wymagane w rozproszonych miejscach aplikacji. Nale\u017cy jednak pami\u0119ta\u0107, \u017ce w przypadku coraz bardziej rozbudowanych serwis\u00f3w, mo\u017cemy straci\u0107 kontrol\u0119 nad tym, jak nasz stan zmienia si\u0119 w czasie. Gdy widzimy, \u017ce nasz g\u0142\u00f3wny komponent zaczyna by\u0107 opakowywany w coraz wi\u0119cej kontekst\u00f3w, a ka\u017cdy z nich zawiera poka\u017an\u0105 liczb\u0119 funkcji aktualizuj\u0105cych stan, wtedy najwy\u017csza pora zwr\u00f3ci\u0107 si\u0119 do naszego starego przyjaciela, <strong>Reduxa<\/strong>.<\/p>\n\n\n\n<p>***<\/p>\n\n\n\n<p>Je\u015bli interesuje Ci\u0119 tematyka Reacta i Fluxa, zajrzyj r\u00f3wnie\u017c <a href=\"https:\/\/sii.pl\/blog\/wyszukiwarka\/react\/\" target=\"_blank\" aria-label=\"do innych artyku\u0142\u00f3w naszych ekspert\u00f3w (opens in a new tab)\" rel=\"noreferrer noopener\" class=\"ek-link\">do innych artyku\u0142\u00f3w naszych ekspert\u00f3w<\/a>. <\/p>\n\n\n<div class=\"kk-star-ratings kksr-auto kksr-align-left kksr-valign-bottom\"\n    data-payload='{&quot;align&quot;:&quot;left&quot;,&quot;id&quot;:&quot;21247&quot;,&quot;slug&quot;:&quot;default&quot;,&quot;valign&quot;:&quot;bottom&quot;,&quot;ignore&quot;:&quot;&quot;,&quot;reference&quot;:&quot;auto&quot;,&quot;class&quot;:&quot;&quot;,&quot;count&quot;:&quot;5&quot;,&quot;legendonly&quot;:&quot;&quot;,&quot;readonly&quot;:&quot;&quot;,&quot;score&quot;:&quot;5&quot;,&quot;starsonly&quot;:&quot;&quot;,&quot;best&quot;:&quot;5&quot;,&quot;gap&quot;:&quot;11&quot;,&quot;greet&quot;:&quot;&quot;,&quot;legend&quot;:&quot;5\\\/5 ( votes: 5)&quot;,&quot;size&quot;:&quot;18&quot;,&quot;title&quot;:&quot;Zarz\u0105dzanie stanem w React&quot;,&quot;width&quot;:&quot;139.5&quot;,&quot;_legend&quot;:&quot;{score}\\\/{best} ( {votes}: {count})&quot;,&quot;font_factor&quot;:&quot;1.25&quot;}'>\n            \n<div class=\"kksr-stars\">\n    \n<div class=\"kksr-stars-inactive\">\n            <div class=\"kksr-star\" data-star=\"1\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"2\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"3\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"4\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"5\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n    \n<div class=\"kksr-stars-active\" style=\"width: 139.5px;\">\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n<\/div>\n                \n\n<div class=\"kksr-legend\" style=\"font-size: 14.4px;\">\n            5\/5 ( votes: 5)    <\/div>\n    <\/div>\n","protected":false},"excerpt":{"rendered":"<p>Rosn\u0105ca popularno\u015b\u0107 architektury Flux oraz implementuj\u0105cego j\u0105 Reduxa sprawi\u0142a, \u017ce praktycznie w ka\u017cdym projekcie opartym na React mo\u017cemy spotka\u0107 wspomnian\u0105 &hellip; <a class=\"continued-btn\" href=\"https:\/\/sii.pl\/blog\/zarzadzanie-stanem-w-react\/\">Continued<\/a><\/p>\n","protected":false},"author":507,"featured_media":21279,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_editorskit_title_hidden":false,"_editorskit_reading_time":9,"_editorskit_is_block_options_detached":false,"_editorskit_block_options_position":"{}","inline_featured_image":false,"footnotes":""},"categories":[1314],"tags":[2427,1591,1546,991],"class_list":["post-21247","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-development-na-twardo","tag-digital","tag-flux","tag-przeglad-narzedzi","tag-react"],"acf":[],"aioseo_notices":[],"republish_history":[],"featured_media_url":"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/04\/Zarzadzanie-stanem-w-React.jpg","category_names":["Development na twardo"],"_links":{"self":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/21247"}],"collection":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/users\/507"}],"replies":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/comments?post=21247"}],"version-history":[{"count":3,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/21247\/revisions"}],"predecessor-version":[{"id":21321,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/21247\/revisions\/21321"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/media\/21279"}],"wp:attachment":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/media?parent=21247"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/categories?post=21247"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/tags?post=21247"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}