Egyszerű komponensek szintjén általában problémamentesen működik a beépített state megoldás.
Nézzük a legegyszerűbb példát a dokumentációból:
Itt ugye csak annyi történik, hogy a gomb onClick eventje frissíti a count állapotát, az értékéhez hozzáad 1-et. Magát az állapotot láthatóan egy
tagben olvassuk ki, így az az elem biztosan újra fog tölteni a változásra. Ami elsőre nem logikus, hogy itt igazából a teljes CounterButton komponens újra fog renderelni, tehát a benne lévő div is és a p-vel egy szinten lévő button is, továbbá a “name” nevű változónál is újra megtörténik az értékadás, hiába definiáltuk konstansként. Ez azért van, mivel a CounterButton egy funkcionális komponens (függvény alapú). Felfoghatjuk úgy is, mintha a statek változása esetén a CounterButton, mint függvény meg lenne hívva. Értelemszerűen egy függvény meghívása esetén az az elejétől kezdve újra fog futni. Ez egy ilyen egyszerű esetben nem okoz problémát, viszont komplexebb komponenseknél már a felhasználó által is látható lenne a statek változása és az ebből fakadó újra renderelés, ha ezt külön nem kezeljük le.
Bonyolultabb appoknál gyakran felmerül a state-ek továbbadása belső komponensek számára. Ennek a legegyszerűbb formája a propként átadás.
Az előző példához közel maradva tehát ha egy töltés animációt szeretnénk egy gombra rakni, akkor valahogy így fog kinézni:
Egy törlési kérelem elküldésekor a loader megjelenik a gombon a state alapján és egészen addig ott is marad, amíg a fetch-re nem érkezik válasz a backendtől.
Ha ezt gyakran változó adatokhoz használjuk, ráadásul nem csak 1-2 komponensen keresztül passolunk egy ilyen propot és nem csak 1 helyen változtatjuk (hiszen éppen ezért passoltuk egy magasabb szintről), akkor elég hamar átláthatatlan lesz a működés, mert minden amin keresztül átadtuk ezt, újra fog renderelni.
Az átláthatóság javítására használhatunk Context API-t, aminek köszönhetően nem kell átadni az azonos kontextuson belül lévő komponensek között az állapotokat.
Maga a Context API része a Reactnek, amelynek köszönhetően csomagtelepítés és a bundle size növelése nélkül tudjuk használni.
A következő példa nyilván az állatorvosi ló példája, de a működés bemutatására elegendő. Egy ilyen egyszerű funkció megvalósítására a való életben természetesen nem használnánk a Context API-t, látható, hogy aránylag sok kódra van szükségünk a megvalósításhoz.
A Context API használatához először létre kell hoznunk magát a contextet, majd egy providert, ami biztosítani fogja a hozzáférést a contexthez azokon a komponenseken belül, amelyek “wrappelve” vannak a providerbe.
Az app staten belül létrehoztunk egy loading mezőt, ami a példában azt határozza meg, hogy éppen tölt-e az alkalmazás. Ha több adat lekérése szükséges minden friss indításnál, akkor érdemes a kezdeti állapotot “true”-ként definiálni.
Mivel a Context API használata akkor ajánlott, ha több helyen hozzá kell férnünk egy adott statehez, ezért érdemes egy egyedi hookot készíteni, ami biztosítani fogja a hozzáférést a contexthez. Így a használat helyén nem kell majd importálnunk az AppContextet, és a useContextet, elég a saját useAppContext hookunk, ezzel is növelve az átláthatóságot.
Miután elkészült a context providerünk, wrappeljük bele azokat a komponenseket, amikben szeretnénk, hogy elérhető legyen a context. A példában az egész app hozzá fog férni, kivéve természetesen maga az App komponens (Mivel abban történik a wrappelés).
A Frame komponenst felfoghatjuk az egész alkalmazás kereteként. Látható, hogy a useAppContext egyedi hookunk által könnyen hozzáférünk a loading statehez. Ennek köszönhetően mondhatjuk azt, hogy ha a loading state true, akkor egy Loader komponenst jelenítünk meg, minden más esetben pedig magát a tartalmat.
Mivel a statek sajátossága az, hogy változtatásuk esetén újrarenderel a hozzájuk tartozó komponens, így a Context API használatával is ugyanez történik. Tehát ha a provideren belül megváltozik a loading stateünk, akkor minden, a provideren belül lévő komponens újra fog renderelni. Ha ezt a működést szeretnénk elkerülni, abban az esetben még tovább kell mennünk a Reactes state kezelésben, itt jön a képbe a Redux.
Az újra renderelés problémáját azonban a Context API nem oldja meg sajnos. Az egyik legismertebb megoldás lehet a React Redux könyvtár használata.
A Reduxot nagyjából úgy képzeljük el, mint egy okos kliensoldali adatbázist, amelybe bárhonnan írhatunk és bárhonnan olvashatjuk, ezzel felváltva a state-ek használatát.
A gyakorlatban ez úgy néz ki, hogy létrehozunk egy storet (ebből egyébként több is lehet egyszerre), amelyben tárolni fogjuk az adatokat.
Példánkban NextJS-t használunk. Az Appunkat beletesszük egy Providerbe, amely megkapja a storet propként, ami a useWrappedStore hook segítségével olvasható ki. Ennek köszönhetően minden ami az Appban van, eléri majd a storet.
Adat beírást a dispatch függvény használatával tudunk elindítani, amellyel elindul egy action, amelynek köszönhetően a reducerben lefut az adott actionnek megfelelő rész, bekerül az adat a storeba.
Ez lesz a reducerünk:
Ha meghívódik egy action akkor a reducer function lefut és az action típusától függően módosítja a storeban a state-et. Esetünkben az isAuthenticated fog átállni majd, amikor lefut az alábbi action:
Itt nincs semmi varázslat, megadjuk hogy a reducer milyen típust kapjon meg, illetve beletesszük a konkrét adatot amit szeretnénk beírni. Ezután pedig már csak dispatchelni kell:
És az adatunk be is került a storeba. Ha kiolvasni szeretnénk, akkor komponensekben való használathoz a useSelector hookot tudjuk használni, melyet az adott elem kulcsával címzünk.
Ezután a selectorból kijövő adatot már kezelhetjük úgy, mintha egy sima state lenne, lokálisan egy komponensen belül. Ennek az egésznek az a nagy előnye, hogy így csak az a komponens fog újrarenderelni amibe a selectort írtuk, a parent elemek nem. Tekinthetünk rá úgy, mintha ez egy lokális state lenne, de globális scopeban is elérjük.
A hátránya ezek után egyértelműen az, hogy egyszerű appoknál túl sok plusz kódot kell gyártani, így nincs értelme élből mindenre Reduxot használni.
Ha elakadtál a fejlesztéssel vagy valamilyen kérdésed van, keresd bizalommal alkalmazás fejlesztő kollégáinkat!