A React számos fejlesztő számára a dinamikus kliensoldali alkalmazások építésének megfelelő keretrendszere. Ezeknek az alkalmazásoknak a dinamikus jellege a rugalmasságból, valamint az ügyféloldalon elérhető képességek és szolgáltatások kibővített listájából fakad, ami lehetővé tette a fejlesztők számára, hogy pillanatok alatt teljes értékű alkalmazásokat készítsenek, amelyek pillanatok alatt betöltődnek a böngészőbe, ami nem volt lehetséges (vagy nagyon nehézkes) a statikus web napjaiban.
A lehetőségek e bővülésével megjelent az állapotkezelés koncepciója, mivel az ügyféloldali alkalmazások komplexitásának növekedésével a helyi állapot megtartásának igénye önmagában szűk keresztmetszetté válik, ha nem kezelik megfelelően és nem a méretezhetőséget szem előtt tartva.
Ezzel a kérdéssel számos keretrendszer foglalkozott, különböző megközelítéseket követve és különböző részproblémacsoportokra összpontosítva, ezért fontos a választott keret ökoszisztémájának magas szintű megértése az egyes alkalmazások szükségleteinek felméréséhez és a megfelelő megközelítés alkalmazásához. mérőszámok. Ez a cikk rövid áttekintést nyújt a gyakori állapotkezelési problémákról, és megpróbál különböző megközelítéseket bevezetni (useState, Context API) válaszként. Noha ez a cikk több megoldást is bemutat, csak a kisebb léptékű kihívásokra összpontosít, a következő cikkekben haladóbb témákkal foglalkozunk.
Hitelesítési munkafolyamat
A cikkben bemutatott kód itt.
Az élő előnézeti link itt érhető el.
Tekintsük azt az esetet, amikor egy React alkalmazás hitelesítési folyamatát hajtjuk végre.
A fenti GIF-en látható módon lehetővé akarjuk tenni a felhasználók számára, hogy hitelesítő adatokkal bejelentkezzenek vagy regisztráljanak az alkalmazásunkba. Érvényes hitelesítő adatok megadása esetén a felhasználó bejelentkezik, az alkalmazás automatikusan a kezdőlapra navigál, és a felhasználó folytathatja az alkalmazás használatát.
Hasonlóképpen, ha a felhasználó kijelentkezik, a kezdőlap erőforrásai bejelentkezés mögött védve lesznek, a bejelentkezési oldal lesz az egyetlen oldal, amelyet a felhasználó elérhet.
Ha ezt a munkafolyamatot a megvalósítás szempontjából tekintjük, akkor egy App nevű fő komponensünk lenne, az App komponens két oldal egyikére irányítja át a felhasználót: Kezdőlap vagy Bejelentkezés, a felhasználó aktuális állapota (bejelentkezett, kijelentkezett) határozza meg, hogy melyik oldalra. oldal, amelyre a felhasználó át van irányítva, a felhasználó aktuális állapotának változása (például bejelentkezettről kijelentkezettre vált) azonnali átirányítást kell, hogy indítson a megfelelő oldalra.
Ahogy a fenti ábrán is látható, azt szeretnénk, hogy az App komponens figyelembe vegye az aktuális állapotot, és a két oldal közül csak az egyiket jelenítse meg – a Kezdőlap vagy a Bejelentkezés – az aktuális állapot alapján.
Ha a felhasználó nulla, az azt jelenti, hogy nincs hitelesített felhasználónk, ezért automatikusan a bejelentkezési oldalra lépünk, és feltételes megjelenítéssel védjük a kezdőlapot. Ha a felhasználó létezik, akkor az ellenkezőjét csináljuk.
Most, hogy tisztában vagyunk azzal, hogy mit kell megvalósítani, vizsgáljunk meg néhány lehetőséget, de először állítsuk be a React projektünket,
A projekt repo tartalmaz egy minta háttéralkalmazást, amelyet a frontend oldal megvalósítására fogunk használni (nem megyünk bele, mert itt nem ez a fő hangsúly, de a kódot szándékosan egyszerűnek tartottuk, így nem lesz nehéz dolga )
Kezdjük a következő oldalak és összetevők létrehozásával:
-
Kezdőlap
-
Bejelentkezési oldal
-
EditUser oldal
-
Navbar komponens
-
UserDropdown komponens
A többoldalas React alkalmazásnak megfelelő navigációra van szüksége, ehhez a react-router-dom segítségével létrehozhatunk egy globális böngésző útválasztó kontextust és regisztrálhatunk különböző React útvonalakat.
yarn add react-router-dom
Tudjuk, hogy sok olvasó jobban szereti a követést és az oktatóanyagokat, ezért itt van a kezdősablon, amely felgyorsítja a lépést. Ez a kezdő ág a DaisyUI-t használja az előre meghatározott TailwindCSS JSX-komponensekhez. Tartalmazza az összes összetevőt, oldalt és a már beállított útválasztót. Ha azt fontolgatja, hogy követi ezt az oktatóanyagot, és saját maga építi fel a teljes hitelesítési folyamatot egyszerű lépések követésével, először a tárhely elágazásával kezdje. Miután elágazta a tárat, klónozza azt, és kezdje a start-hereelágazással:
git clone git@github.com:<your-username>/fullstack-resourcify.git
Miután meghúzta a start-itt ágat:
-
Nyissa meg a projektet a kívánt kódszerkesztővel
-
Módosítsa a könyvtárat a következőre: frontend/
-
Függőségek telepítése: fonal
-
Indítson el egy fejlesztői szervert: yarn dev·
Az előnézetnek valahogy így kell kinéznie:
A Navbarban megjelenített név – jobb felső sarokban – egy állapotváltozó, amelyet az alkalmazás fő összetevőjében határoztak meg. Ugyanaz a változó kerül átadásra a Navbarnak és a Kezdőlapnak is. A fent használt egyszerű űrlap valójában frissíti a „name” állapotváltozót az EditPage komponensből.
Az alábbiakban bemutatott két megközelítés a végrehajtás részleteire vonatkozik:
Első megközelítés: useState
useState()
A useState az egyik leggyakrabban használt React hook, lehetővé teszi állapot létrehozását és mutációját a React Functional komponensben. A useState komponens nagyon egyszerű megvalósítású és könnyen használható: új állapot létrehozásához meg kell hívni a useState-et az állapot kezdeti értékével, és a useState hook két változót tartalmazó tömböt ad vissza: az első az állapot változó, amellyel az állapotára hivatkozhat, a második pedig egy függvény, amellyel megváltoztathatja az állapot értékét: elég egyszerű.
Mit szólnál, ha ezt működés közben látnánk? A Navbarban megjelenített név – jobb felső sarokban – egy állapotváltozó, amelyet az alkalmazás fő összetevőjében határoztak meg. Ugyanaz a változó kerül átadásra a Navbarnak és a Kezdőlapnak is. A fent használt egyszerű űrlap valójában frissíti a „name” állapotváltozót az EditPage komponensből. A lényeg a következő: a useState egy alapvető horog, amely elfogad egy kezdeti állapotot paraméterként, és két változót ad vissza, amelyek két értéket tartalmaznak, a kezdeti állapotot tartalmazó állapotváltozót és ugyanahhoz az állapotváltozóhoz egy beállító függvényt.
Bontsuk fel, és nézzük meg, hogyan valósították meg először.
- A „name” állapotváltozó létrehozása:
./src/App.jsx
import { useState } from "react";
function App() {
const testValue = "CLA";
//using the useState hook to create a state variable out of an initial value passed as an argument
const [name, setName] = useState(testValue);
console.log(`Rendering: ${name}`);
return (...)};
Kellékek
A kellékek a react komponens egyik alapvető építőkövei, fogalmilag, ha a React funkcionális komponenst Javascript függvénynek tekinti, akkor a kellékek nem mások, mint a függvény paraméterei, a kellékek és a useState hook kombinálása szilárd keretet kínálhat. állapot kezeléséhez egy egyszerű React alkalmazáson keresztül.
A React kellékek attribútumként kerülnek átadásra az egyéni összetevőknek. A kellékként átadott attribútumok destrukturálhatók a props objektumból, ha argumentumként fogadjuk el, ehhez hasonlóan:
Kellékek átadása
<Routes>
<Route path="/" element={<Home name={name} />} /> // Passing name as a prop to
Home Component
<Route
path="/user"
element={<EditUser name={name} setName={setName} />} // passing both name and setItem function as props to EditUser component
/>
<Route path="/login" element={<Login />} />
</Routes>
A kellékek a normál függvény argumentumokhoz hasonlóan funkcionális komponensen belül is elfogadhatók és használhatók. Ez azért van, mert a „név” a Home komponensnek kellékként van átadva, így ugyanabban a komponensben jeleníthetjük meg. A következő példában elfogadjuk az átadott propot a destructuring szintaxis használatával, hogy kivonjuk a name tulajdonságot a props objektumból.
Kellékek elfogadása
./src/pages/Home.jsx
... ...
export default function Home({name}) { //Destructuring the name property from the props object, another approach would be: Home(props.name)
console.log("Rendering: Home");
return (
<div className="flex flex-col bg-white m-auto p-auto">
<h1 className="flex py-5 lg:px-20 md:px-10 mx-5 font-bold text-2xl text-gray-800">
Welcome {name}
</h1>
... ...
Profi tipp
Nyissa meg a böngésző konzolját, és figyelje meg, hogy az állapot megváltozásakor a „name”-t használó összes összetevő hogyan jelenik meg újra. Az állapotváltozók manipulálásakor a React eltárolja a következő állapotot, újra rendereli az összetevőt az új értékekkel, és frissíti a felhasználói felületet.
useState hátrányok
Kellékek-fúrás
A kellékek fúrása egy olyan kifejezés, amely az összetevők hierarchiájára utal, ahol egy komponenskészlethez egy szülőkomponens által biztosított bizonyos kellékekre van szükség. Egy gyakori megoldás, amelyet a tapasztalatlan fejlesztők általában úgy használnak, hogy ezeket a kellékeket a teljes komponensláncon keresztül továbbítják. Az a megközelítés, hogy ezen kellékek bármelyikének megváltoztatása a komponensek teljes láncának újrarenderelését váltja ki, ami hatékonyan lelassítja az egész alkalmazást a szükségtelen renderelések következtében, a lánc közepén lévő komponenseket, amelyekhez nincs szükség ezekre a kellékekre. médiumként működnek a kellékek átviteléhez.
Példa:
-
Az állapotváltozót a useState() hook segítségével határozták meg a fő alkalmazáskomponensben
-
A Navbar komponensnek egy névjavaslatot adtak át
-
Ugyanaz a javaslat elfogadva a Navbarban, és újra átadva propként a UserDropdown komponensnek
-
A UserDropdown az utolsó gyermekelem, amely elfogadja a propot.
./src/App.jsx
... ...
function App() {
const [name, setName] = useState(test);
console.log("Rendering: App");
return (
<BrowserRouter>
<div className="h-screen">
<Navbar name={name} />
<main className="px-4">
... ...
./src/components/Navbar.jsx
import React from "react";
import Logo from "../assets/cla.svg";
import { BiSearchAlt } from "react-icons/bi";
import { Link } from "react-router-dom";
import UserDropdown from "./UserDropdown";
export default function Navbar({ name }) {
console.log("Rendering: Navbar");
return (
<>
<div className="navbar bg-base-100 drop-shadow-sm">
<div className="flex-1">
<Link
to="/"
className="btn btn-ghost normal-case text-md md:text-xl px-2 gap-1"
>
<img src={Logo} className="h-6" alt="" />
Resources
</Link>
</div>
<UserDropdown name={name} />
</div>
</>
);
}
Képzelje el, milyen nehéz olyan React alkalmazást karbantartani, ahol az összetevők különböző rétegei mindegyike ugyanazt az állapotot adja át és jeleníti meg.
Növekvő komplexitás és kódminőség
A useState és a props használatával, mint az állapotkezelés egyetlen eszközével egy react alkalmazásban, a kódbázis gyorsan bonyolultabbá válhat, és több tíz vagy száz állapotváltozót kell kezelnie, amelyek egymás duplikátumai lehetnek, szétszórva különböző fájlok között és A komponensek meglehetősen ijesztőek lehetnek, egy adott állapotváltozó bármilyen megváltoztatása megköveteli az összetevők közötti függőségek alapos mérlegelését, hogy elkerüljük az esetleges további újrarenderelést egy amúgy is lassú alkalmazásban.
Második megközelítés: Context API
A Context API a React kísérlete arra, hogy megoldja a csak props és useState használatának hátrányait az állapotkezeléshez, különösen, a kontextus API választ ad arra a korábban említett problémára, hogy a kellékeket át kell adni a teljes komponensfán. A kontextus használatával meghatározhat egy állapotot a globálisnak ítélt adatokhoz, és az összetevőfa bármely pontjáról hozzáférhet az állapotához: nincs több támasztófúrás.
Fontos kiemelni, hogy a kontextus API-t eredetileg arra tervezték, hogy megoldja a globális adatmegosztás problémáját, az adatok, például a felhasználói felület témái, a hitelesítési információk, amelyek a mi használati esetünk, a nyelvek és hasonlók), más típusú adatok megosztására. egynél több összetevő közül, de nem feltétlenül globális az összes alkalmazásra, előfordulhat, hogy a kontextus nem a legjobb megoldás, a használati esettől függően más technikákat is fontolóra vehet, például összetevő összetétele, amely nem tartozik e cikk hatálya alá.
Váltás kontextusra
A hitelesítési környezet létrehozása
A createContext egyszerű, új környezeti változót hoz létre, egyetlen opcionális paramétert vesz fel: a kontextusváltozó alapértelmezett értékét.
Lássuk ezt működés közben, először hozzon létre egy új „contexts” mappát, és egy új fájlt az „Auth.jsx” belsejében. Új kontextus létrehozásához meg kell hívnunk a createContext() függvényt, és a visszaadott értéket hozzá kell rendelnünk egy új Auth változóhoz, amelyet a következőképpen exportálunk:
./src/contexts/Auth.jsx
import { createContext } from "react";
export const Auth = createContext();
Adja meg a hitelesítési kontextust
Most meg kell jelenítenünk a korábban létrehozott "Auth" kontextusváltozót, ennek eléréséhez a kontextusszolgáltatót használjuk, a kontextusszolgáltató egy olyan komponens, aminek van "érték" propja, ezzel a tulajdonsággal megoszthatunk egy értéket - név – az összetevőfán keresztül az összetevőfát a szolgáltató komponenssel burkolva az adott értéket a komponensfán belül bárhonnan elérhetővé tesszük anélkül, hogy ezt a támasztékot külön-külön át kellene adni az egyes alárendelt összetevőknek.
A hitelesítési példánkban szükségünk van egy változóra a felhasználói objektum tárolására, és valamilyen beállítóra az állapotváltozó manipulálásához, ez a useState tökéletes használati esete. A Context használatakor meg kell győződnie arról, hogy definiálja a megadni kívánt adatokat, és átadja ezeket az adatokat – a példánkban felhasználónak – az összes benne beágyazott összetevőfának, tehát definiáljon egy új felhasználó állapotváltozót a szolgáltató összetevőn belül, és végül a usert és a setUser-t is átadjuk egy tömbön belül, mint az az érték, amelyet a szolgáltató az összetevőfának tesz közzé:
./src/contexts/Auth.jsx
import { createContext, useState } from "react";
export const Auth = createContext();
export const AuthProvider = ({ children }) => {
const [user, setUser] = useState(null);
return <Auth.Provider value={[user, setUser]}>{children}</Auth.Provider>;
};
Egy másik dolog, amit tehetünk, hogy áthelyezzük a „name” állapotváltozónkat az alkalmazás fő összetevőjéből az Auth kontextusba, és beágyazott összetevőknek tesszük ki:
import { createContext, useState } from "react";
export const Auth = createContext();
export const AuthProvider = ({ children }) => {
const [user, setUser] = useState(null);
const [name, setName] = useState("CLA");
return <Auth.Provider value={[name, setName]}>{children}</Auth.Provider>;
};
Most már csak az van hátra, hogy beágyazzuk az alkalmazásunkat ugyanabba az AuthProvider-komponensbe, amelyet éppen exportáltunk.
./src/main.jsx:
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { AuthProvider } from "./contexts/Auth";
import "./index.css";
ReactDOM.createRoot(document.getElementById("root")).render(
<AuthProvider>
<App />
</AuthProvider>
);
Mivel az Auth.Provider-en belül jelenítjük meg a gyermek propokat, az AuthProvider összetevőbe beágyazott összes elem képes felhasználni az Auth.Provider-nek átadott értéket. Zavarba ejtőnek tűnhet, de ha egyszer kísérletezik vele, próbáljon meg egy globális – Kontextus – állapotot biztosítani és felhasználni. Végül is csak a Context API-val való kísérletezés után volt értelme számomra.
A hitelesítési kontextus felhasználása
Az utolsó lépés egyszerű, a "useContext" kontextus hook segítségével elérjük az "Auth" kontextusszolgáltatója által biztosított értéket, ami esetünkben a user és a setUser tömbje, a következő kódban képesek vagyunk használja a useContext-et a navigációs sávon belüli hitelesítési kontextus felhasználásához. Ez csak azért lehetséges, mert a Navbar az App komponensbe van beágyazva, és mivel az AuthProvider körbeveszi az App komponenst, az értéktámasz csak a useContext hook használatával használható fel. Egy másik nagyszerű eszköz, a React már a dobozból kiindulva biztosít minden olyan adat kezelését, amelyhez globálisan hozzá lehet férni, és bármely fogyasztói összetevővel manipulálható.
./src/components/Navbar.jsx
export default function Navbar() {
const [name, setName] = useContext(Auth);
console.log(name)
return (...)};
Figyelje meg, hogy a Navbar() funkcionális komponensben már nem fogadunk el kellékeket. Ehelyett a useContext(Auth) függvényt használjuk az Auth kontextus felhasználására, mind a name, mind a setName megragadásával. Ez azt is jelenti, hogy többé nem kell átadnunk a kelléket a Navbarnak:
./src/App.jsx
// ... //
return (
<BrowserRouter>
<div className="h-screen">
<Navbar/> // no need to pass prop anymore
// ... //
A hitelesítési kontextus frissítése
A megadott setName függvényt is használhatjuk a „name” állapotváltozó manipulálására:
./src/pages/EditUser.jsx
export default function EditUser() { // no need to accept props anymore
const [name, setName] = useContext(Auth); // grabbing the name and setName variables from Auth context
console.log("Rendering: EditUser");
A useReducer() hook bemutatása
Az előző példában a kontextus API-t használtuk állapotunk kezelésére és megosztására az összetevőfán, és észrevehette, hogy még mindig a useState-et használjuk logikánk alapjaként, és ez többnyire rendben van, mivel az állapotunk még mindig egy egyszerű objektum. ezen a ponton, de ha bővíteni akarjuk a hitelesítési folyamatunk lehetőségeit, akkor minden bizonnyal többet kell tárolnunk, mint a jelenleg bejelentkezett felhasználó e-mailjeit, és itt térünk vissza azokhoz a korlátokhoz, amelyekbe korábban belementünk. a useState használata összetett állapottal, szerencsére a React megoldja ezt a problémát azáltal, hogy alternatívát kínál a useState helyett az összetett állapot kezelésére: írja be a useReducer-t.
A useReducer a useState általánosított változataként fogható fel, két paraméterre van szükség: egy reduktor funkcióra és egy kezdeti állapotra.
A kezdeti állapothoz nincs semmi érdekes, a varázslat a reduktor funkción belül történik: ellenőrzi a megtörtént művelet típusát, és ettől a művelettől függően a reduktor meghatározza, hogy milyen frissítéseket alkalmazzon az állapotra, és visszaadja az új értékét. .
Az alábbi kódot nézve a reduktor funkciónak két lehetséges művelettípusa van:
-
"BEJELENTKEZÉS": ebben az esetben a felhasználói állapot frissül az új felhasználói hitelesítési adatokkal, amelyek a műveleti adattartalomban találhatók.
-
"LOGOUT": ebben az esetben a felhasználói állapot törlődik a helyi tárolóból, és nullára áll vissza.
Fontos megjegyezni, hogy a műveletobjektum tartalmaz egy típusmezőt, amely meghatározza, hogy milyen logikát alkalmazzon, és egy opcionális hasznos adatmezőt is, amely az adott logika alkalmazásához szükséges adatokat szolgáltatja.
Végül a useReducer hook visszaadja az aktuális állapotot és egy diszpécser függvényt, amellyel egy műveletet továbbítunk a reduktornak.
A logika többi részét illetően ez megegyezik az előző példával:
./src/contexts/Auth.jsx
import { createContext, useEffect, useReducer, useState } from "react";
export const Auth = createContext();
import { createContext, useReducer } from "react";
export const Auth = createContext();
const reducer = (state, action) => {
switch (action.type) {
case "LOGIN":
return { user: action.payload };
case "LOGOUT":
localStorage.removeItem("user");
return { user: null };
default:
break;
}
};
export const AuthProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, {
user: null,
});
return (
<Auth.Provider value={{ ...state, dispatch }}>{children}</Auth.Provider>
);
};
Műveletek küldése a setState beállító függvény használata helyett – pl.: setName –
Ahogy az imént említettük, a diszpécser funkciót használjuk egy művelet átadására a reduktornak, a következő kódban elindítunk egy LOGIN műveletet és a felhasználói e-mail-címet rakományként adjuk meg, most a felhasználói állapot frissül, és ez a változás aktiválódik. a felhasználói állapotra előfizetett összes összetevő újramegjelenítése. Fontos kiemelni, hogy az újbóli renderelés csak akkor indul el, ha tényleges változás következik be az állapotban, nem történik újra renderelés, ha a reduktor ugyanazt az előző állapotot adja vissza.
export default function Login() {
const { dispatch } = useContext(Auth);
const handleLogin = async (e) => {
// Updating the global Auth context
dispatch({ type: "LOGIN", payload: {email: email.current.value} });
};
return (...)};
Bejelentkezés
Profi tipp
Figyelje meg, hogy a sikeres bejelentkezés után kapott felhasználói objektum most hogyan kerül tárolásra a localStorage-ban.
Egyedi horog a bejelentkezéshez
Most, hogy jól tudjuk kezelni a useReducer-t, a bejelentkezési és kijelentkezési logikánkat tovább foglalhatjuk saját különálló hook-okba, a Login hook egyetlen hívásával kezelhetjük a bejelentkezési útvonalra irányuló API-hívást, visszakereshetjük az új felhasználót. hitelesítő adatokat, és tárolja őket a helyi tárhelyen, küldjön egy LOGIN hívást a felhasználói állapot frissítéséhez, miközben a hibakezeléssel foglalkozik:
./src/hooks/useLogin.jsx
import { useContext, useState } from "react";
import { Auth } from "../contexts/Auth";
export const useLogin = () => {
const [error, setError] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const { dispatch } = useContext(Auth);
const login = async (email, password) => {
setIsLoading(true);
setError(null);
try {
const response = await fetch("/api/users/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password }),
});
const json = await response.json();
if (json.name === "Error") {
setError(json.message);
setIsLoading(false);
}
if (!response.ok) {
setIsLoading(false);
setError(json);
}
if (response.ok) {
// Save the user and token in the localstorage
localStorage.setItem("user", JSON.stringify(json));
// Updating the global Auth context
dispatch({ type: "LOGIN", payload: json });
setIsLoading(false);
}
} catch (error) {
console.log(error);
}
};
return { error, isLoading, login };
};
Megjegyzés: az olvasók közül haladóbb React-felhasználók számára felmerülhet a kérdés, hogy miért nem a useReducer lusta inicializálási funkcióját használtuk a felhasználói hitelesítő adatok lekéréséhez, a useReducer elfogad egy harmadik opcionális paramétert, az init függvényt, ezt a funkciót akkor használják, ha alkalmaznunk kell némi logikát, mielőtt megkapjuk az állapot kezdeti értékét, az oka annak, hogy nem ezt választottuk, az aggodalmak egyszerű szétválasztásának kérdése, a kód így könnyebben érthető, és ennek következtében egyszerűbb a karbantartása .
Bejelentkezési oldal
Így néz ki a bejelentkezési oldalunk felső része, miután a useLogin() hook segítségével kibontottuk a bejelentkezési függvényt, és meghívtuk a bejelentkezési függvényt a felhasználó által megadott hitelesítő adatokkal:
// ... ... //
export default function Login() {
const { login, isLoading, error } = useLogin();
console.log("Rendering: Login");
const email = createRef(null);
const password = createRef(null);
const handleLogin = async (e) => {
await login(email.current.value, password.current.value);
};
return (...)
// ... ... //
A Küldés funkciót is letiltjuk, amikor a felhasználó elküldi az űrlapot:
<button
onClick={handleLogin}
disabled={isLoading}
className="btn btn-square w-full bg-gray-100 text-gray-600 hover:bg-gray-300 border-none"
>
{isLoading && "A moment please!"}
{!isLoading && "Login"}
</button>
És a háttérrendszerünktől kapott hitelesítési hibák megjelenítése:
{
error && <span className="text-red-500 p-2">{error.message}</span>;
}
A felhasználói állapot fenntartása
Felmerülhet benned a kérdés, hogy miért kell a felhasználói objektumot a localStorage-ban tárolnunk, egyszerűen fogalmazva, addig akarjuk bejelentkezve tartani a felhasználót, amíg a token le nem jár. A localStorage használata nagyszerű módja a JSON bitek tárolásának, akárcsak a példánkban. Figyelje meg, hogyan törlődik az állapot, ha frissíti az oldalt bejelentkezés után. Ez egyszerűen megoldható a useEffect hook segítségével, amellyel ellenőrizheti, hogy van-e tárolt felhasználói objektum a localStorage-ban, ha van, akkor automatikusan bejelentkezünk a felhasználóba:
// ... ... //
export const AuthProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, {
user: null,
});
useEffect(() => {
const user = JSON.parse(localStorage.getItem("user"));
if (user) {
return dispatch({ type: "LOGIN", payload: user });
}
}, []);
return (
<Auth.Provider value={{ ...state, dispatch }}>{children}</Auth.Provider>
);
};
Egyedi horgok a kijelentkezéshez
Ugyanez vonatkozik a Kijelentkezési hookra is, itt egy LOGOUT műveletet küldünk el, amely eltávolítja az aktuális felhasználói hitelesítő adatokat mind az állapotról, mind a helyi tárhelyről:
./src/hooks/useLogout.jsx
import { useContext } from "react";
import { Auth } from "../contexts/Auth";
export const useLogout = () => {
const { dispatch } = useContext(Auth);
const logout = () => {
// delete user from the localstorage
localStorage.removeItem("user");
// Wipe out the Auth context (user:null) / dipatch 'LOGOUT'
dispatch({ type: "LOGOUT" });
};
return { logout };
};
Felhasználói kijelentkezés
Egy felhasználó kijelentkeztetéséhez adjunk hozzá egy kattintási eseményt a UserDropdown.jsx fájlban található Kijelentkezés gombra, és ennek megfelelően kezeljük:
./src/components/UserDropdown.jsx
// Extracting the logout function from useLogout() and handling the click event listener //
export default function UserDropdown() {
const { user } = useContext(Auth);
const { logout } = useLogout();
console.log("Rendering: UserDropdown");
const handleLogout = () => {
logout();
};
// ... ... //
// Adding a click event listener to logout button //
<li>
<button onClick={handleLogout}>Logout</button>
</li>
// ... ... //
React Routes védelme
Alkalmazásunk megvalósításának utolsó lépése a globális felhasználói állapot kihasználása a felhasználói navigáció szabályozására, gyors emlékeztető arra, hogy milyen viselkedést kell elérni: kezdetben a felhasználót a bejelentkezési oldal köszönti, ettől kezdve a felhasználó csak a kezdőlapra férhet hozzá. a sikeres bejelentkezés után a felhasználó kijelentkezéskor is a bejelentkezési oldalra kerül.
Ezt a react-router-dom könyvtár segítségével érjük el, 2 útvonal definiálásával: "/" és "/login", a globális hitelesítési állapot segítségével szabályozzuk, hogy melyik komponenst jelenítse meg az egyes útvonalakon., az auth kiértékelés nullára nem hitelesített felhasználót jelent, és fordítva:
./src/App.jsx
import { BrowserRouter, Navigate, Route, Routes } from "react-router-dom";
import "./App.css";
import Navbar from "./components/Navbar";
import Home from "./pages/Home";
import Login from "./pages/Login";
import { useContext } from "react";
import { Auth } from "./contexts/Auth";
function App() {
const { user } = useContext(Auth);
return (
<BrowserRouter>
<div className="h-screen">
<Navbar />
<main className="px-4">
<Routes>
<Route
path="/"
element={user ? <Home /> : <Navigate to="/login" />}
/>
<Route
path="/login"
element={!user ? <Login /> : <Navigate to="/" />}
/>
</Routes>
</main>
</div>
</BrowserRouter>
);
}
export default App;
Összefoglaló diagram
Befejezés
Ebben a cikkben megpróbáltuk kezelni az állapotkezelés megvalósításának egyszerű, de nagyon gyakori esetét egy hitelesítési munkafolyamathoz, áttekintve a különböző megközelítéseket, azok mögött meghúzódó indokokat és kompromisszumokat. Az állapotkezelés az ügyféloldali keretrendszerekben és különösen a Reactban az egyik legtöbbet emlegetett téma a frontend közösségben, egyszerűen azért, mert növelheti vagy megtörheti az alkalmazás teljesítményét és méretezhetőségét. A különböző technikák, minták, könyvtárak és eszközök puszta mennyisége, amelyek megpróbálják megoldani ezt az államigazgatási kérdést, elsöprő, célunk az volt, hogy alapos ismereteket adjunk a gyakorlatokról, hogy saját alkalmazásaiban alkalmazhassa, fejlettebbek számára. Az állapotkezelés technikái és mintái összetettebb alkalmazásokban, tekintse meg következő cikkünket, ahol a React méretezhető állapotkezelésének reduxjával foglalkozunk.
Hamarosan
ContextAPI vs Redux Toolkit
A Reduxot sok éven át alkalmazták a közösségben a Redux eszközkészlet előtt, valójában az RTK-t kezdősablonként vezették be a redux állapotkezelés elindításához az új alkalmazásokban (az eredeti neve „redux-starter-kit” volt 2019 októberében), bár ma általános konszenzus van a Redux karbantartói és a közösség között abban, hogy a Redux eszköztár a legmegfelelőbb módja a redux-szal való munkavégzésnek. Az RTX a Redux logikájának nagy részét elvonja, ami megkönnyíti a használatát, sokkal kevésbé bőbeszédű, és arra ösztönzi a fejlesztőket, hogy Kövesse a bevált gyakorlatokat, az egyik fő különbség a kettő között az, hogy a Reduxot úgy építették, hogy ne legyen véleménynyilvánítása, minimális API-t biztosítva, és a fejlesztőktől elvárják, hogy saját programkönyvtárakat írjanak a gyakori feladatokhoz, és foglalkozzanak a kódszerkezettel. ez lassú fejlesztési időt és zűrzavaros kódot eredményezett, a Redux eszközkészletet extra absztrakciós rétegként adták hozzá, amely megakadályozza, hogy a fejlesztők beleesjenek a gyakori buktatókba. További információkért és a karbantartói indoklásáért tekintse meg a hivatalos dokumentációt itt.