React je základní rámec pro vytváření dynamických aplikací na straně klienta pro mnoho vývojářů. Dynamická povaha těchto aplikací pochází z flexibility a rozšířeného seznamu možností a funkcí, které jsou možné na straně klienta, což vývojářům umožnilo vytvářet plnohodnotné aplikace, které se načítají do prohlížeče během několika sekund, což je výkon, který nebyl možné (nebo velmi těžkopádné) v době statického webu.
S tímto rozšířením možností přišel koncept řízení stavu, protože složitost aplikací na straně klienta roste a potřeba udržovat místní stav roste, aby se sám o sobě stal úzkým hrdlem, pokud není správně zpracován a koncipován s ohledem na škálovatelnost.
Tento problém řešilo mnoho rámců, které sledovaly různé přístupy a zaměřovaly se na různé sady dílčích problémů, proto je důležité dobře porozumět ekosystému rámce výběru, abyste mohli posoudit potřeby každé aplikace a použít správný přístup po těchto metriky. Tento článek vám poskytne stručný přehled běžných problémů správy stavu a pokusí se zavést různé přístupy (useState, Context API) jako odpověď na ně. I když tento článek představí několik řešení, zaměří se pouze na výzvy v menším měřítku, pokročilejším tématům se budeme věnovat v nadcházejících článcích.
Pracovní postup ověřování
Kód uvedený v celém článku lze nalézt zde.
Odkaz na živý náhled je dostupný zde.
Zvažte případ, kdy implementujeme proces ověřování pro aplikaci React.
Jak je znázorněno na obrázku GIF výše, chceme uživatelům umožnit přihlášení nebo registraci do naší aplikace pomocí přihlašovacích údajů. Pokud byly zadány platné přihlašovací údaje, uživatel bude přihlášen, aplikace automaticky přejde na domovskou stránku a uživatel může pokračovat v používání aplikace.
Podobně, pokud se uživatel odhlásí, zdroje domovské stránky budou chráněny i po přihlášení, přihlašovací stránka bude jedinou stránkou přístupnou uživateli.
Když přemýšlíme o tomto pracovním postupu z hlediska implementace, měli bychom hlavní komponentu s názvem App, komponenta App přesměruje uživatele na jednu ze dvou stránek: Domovská stránka nebo Přihlášení, aktuální stav uživatele (přihlášený, odhlášený) bude určovat, která stránku, na kterou je uživatel přesměrován, změna aktuálního stavu uživatele (například změna z přihlášeného na odhlášeného) by měla vyvolat okamžité přesměrování na odpovídající stránku.
Jak je znázorněno na obrázku výše, chceme, aby komponenta aplikace zvážila aktuální stav a vykreslila pouze jednu ze dvou stránek – Domovská stránka nebo Přihlášení – na základě tohoto aktuálního stavu.
Pokud je uživatel null, znamená to, že nemáme žádného ověřeného uživatele, takže automaticky přejdeme na přihlašovací stránku a chráníme domovskou stránku pomocí podmíněného vykreslování. Pokud uživatel existuje, děláme opak.
Nyní, když dobře rozumíme tomu, co by mělo být implementováno, pojďme prozkoumat několik možností, ale nejprve nastavíme náš projekt React,
Úložiště projektu obsahuje ukázkovou backendovou aplikaci, kterou použijeme k implementaci frontendové strany (nebudeme se jí zabývat, protože zde není hlavní pozornost, ale kód byl záměrně udržován jednoduchý, takže s tím nebudete mít těžkou práci )
Začneme vytvořením následujících stránek a komponent:
-
Domovská stránka
-
Přihlašovací stránka
-
Upravit stránku uživatele
-
Komponenta Navbar
-
Komponenta UserDropdown
Aplikace React s více stránkami potřebuje správnou navigaci, k tomu můžeme použít respond-router-dom k vytvoření globálního kontextu routeru prohlížeče a registraci různých cest React.
yarn add react-router-dom
Víme, že mnoho čtenářů upřednostňuje sledování spolu s návody, takže zde je úvodní šablona, která vám pomůže rychle. Tato počáteční větev používá DaisyUI pro předdefinované komponenty TailwindCSS JSX. Zahrnuje všechny komponenty, stránky a již nastavený router. Pokud uvažujete o pokračování tohoto návodu a vytvoření celého toku ověřování sami podle jednoduchých kroků, začněte nejprve rozvětvením úložiště. Po rozvětvení úložiště jej naklonujte a začněte od začněte-zdevětve:
git clone git@github.com:<your-username>/fullstack-resourcify.git
Jakmile vytáhnete větev start-zde:
-
Otevřete projekt pomocí preferovaného editoru kódu
-
Změňte adresář na frontend/
-
Instalujte závislosti: příze
-
Spusťte vývojový server: yarn dev·
Náhled by měl vypadat nějak takto:
Název vykreslený v Navbar – pravá horní strana – je stavová proměnná definovaná v hlavní komponentě aplikace. Stejná proměnná je předána navigačnímu panelu i domovské stránce. Jednoduchý formulář použitý výše ve skutečnosti aktualizuje stavovou proměnnou „name“ z komponenty EditPage.
Dva níže uvedené přístupy půjdou do detailů implementace:
První přístup: useState
useState()
useState je jedním z nejběžněji používaných háčků React, umožňuje vám vytvářet a mutovat stav v komponentě React Functional. Komponenta useState má opravdu jednoduchou implementaci a snadno se používá: pro vytvoření nového stavu musíte zavolat useState s počáteční hodnotou vašeho stavu a háček useState vrátí pole obsahující dvě proměnné: první je stav proměnná, kterou můžete použít k odkazování na svůj stav, a druhá funkce, kterou používáte ke změně hodnoty stavu: docela jednoduché.
Co kdybychom to viděli v akci? Název vykreslený v Navbar – pravá horní strana – je stavová proměnná definovaná v hlavní komponentě aplikace. Stejná proměnná je předána navigačnímu panelu i domovské stránce. Jednoduchý formulář použitý výše ve skutečnosti aktualizuje stavovou proměnnou „name“ z komponenty EditPage. Sečteno a podtrženo: useState je základní háček, který přijímá počáteční stav jako parametr a vrací dvě proměnné obsahující dvě hodnoty, stavovou proměnnou obsahující počáteční stav a funkci setter pro stejnou stavovou proměnnou.
Pojďme si to rozebrat a podívat se, jak to bylo vůbec implementováno.
- Vytvoření stavové proměnné „name“:
./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 (...)};
Rekvizity
Rekvizity jsou jedním ze základních stavebních kamenů komponenty React, koncepčně, pokud uvažujete o funkční komponentě React jako o funkci Javascript, pak rekvizity nejsou nic víc než parametry funkce, kombinace rekvizit a háku useState vám může nabídnout solidní rámec. pro správu stavu v jednoduché aplikaci React.
React rekvizity jsou předány jako atributy uživatelským komponentám. Atributy předané jako rekvizity mohou být z objektu rekvizity zničeny, když jej přijmete jako argument, podobně jako:
Předávání rekvizit
<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>
Podpěry lze přijímat a používat uvnitř funkční komponenty podobně jako argumenty normální funkce. Je to proto, že „název“ je předán jako rekvizita komponentě Home, takže jej můžeme vykreslit ve stejné komponentě. V následujícím příkladu přijímáme předané prop pomocí destrukční syntaxe k extrahování vlastnosti name z objektu props.
Přijímání rekvizit
./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>
... ...
Profesionální tip
Otevřete konzolu prohlížeče a všimněte si, jak se všechny komponenty používající prop „name“ znovu vykreslují, když se stav změní. Při manipulaci se stavovou proměnnou React uloží další stav, znovu vykreslí vaši komponentu s novými hodnotami a aktualizuje uživatelské rozhraní.
nevýhody stavu použití
Podpěry-vrtání
Vrtání rekvizit je termín označující hierarchii komponent, kde sada komponent potřebuje určité rekvizity poskytované nadřazenou komponentou. Běžným řešením, které obvykle používají nezkušení vývojáři, je předat tyto rekvizity v celém řetězci komponent, problém s tímto přístup spočívá v tom, že změna kterékoli z těchto rekvizit spustí celý řetězec komponent k opětovnému vykreslení, což efektivně zpomalí celou aplikaci v důsledku těchto zbytečných renderů, komponent uprostřed řetězce, které tyto rekvizity nevyžadují. fungují jako média pro přenos rekvizit.
Příklad:
-
Stavová proměnná byla definována v hlavní komponentě aplikace pomocí háčku useState().
-
Komponentě Navbar byla předána podpěra názvu
-
Stejná podpěra přijata v Navbar a předána jako podpěra ještě jednou komponentě UserDropdown
-
UserDropdown je poslední podřízený prvek, který přijímá prop.
./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>
</>
);
}
Představte si, jak těžké je udržovat aplikaci React s různými vrstvami komponent, které procházejí a vykreslují stejný stav.
Rostoucí složitost a kvalita kódu
S použitím useState a rekvizit jako jediného prostředku správy stavu v aplikaci Reakce může kódová základna rychle narůst do složitosti a musí spravovat desítky nebo stovky stavových proměnných, které mohou být navzájem duplikované, rozptýlené v různých souborech a komponenty mohou být docela skličující, jakákoli změna dané stavové proměnné bude vyžadovat pečlivé zvážení závislostí mezi komponentami, aby se předešlo jakémukoli možnému dodatečnému vykreslování v již tak pomalé aplikaci.
Druhý přístup: Kontextové API
Kontextové API je pokus Reactu vyřešit nevýhody pouze používání rekvizit a useState pro správu stavu, konkrétně kontextové API přichází jako odpověď na výše zmíněný problém týkající se potřeby předat rekvizity dolů celým stromem komponent. Pomocí kontextu můžete definovat stav pro data, která považujete za globální, a přistupovat k jejich stavu z libovolného bodu ve stromu komponenty: žádné další vrtání.
Je důležité zdůraznit, že kontextové API bylo původně koncipováno tak, aby řešilo problém globálního sdílení dat, dat, jako jsou témata uživatelského rozhraní, autentizační informace, které jsou naším případem použití, jazyky a podobně), pro jiné typy dat, která je třeba sdílet. mezi více než jednou komponentou, ale nemusí být nutně globální pro všechny aplikace, kontext nemusí být nejlepší volbou, v závislosti na případu použití můžete zvážit jiné techniky, jako je složení komponenty, což je mimo rozsah tohoto článku.
Přepínání do kontextu
Vytvoření kontextu Auth
createContext je přímočarý, vytváří novou kontextovou proměnnou, přebírá jeden volitelný parametr: výchozí hodnotu kontextové proměnné.
Podívejme se na to v akci, nejprve vytvořte novou složku „contexts“ a v ní nový soubor „Auth.jsx“. Abychom vytvořili nový kontext, musíme vyvolat funkci createContext(), přiřadit vrácenou hodnotu nové proměnné Auth, která bude exportována jako další:
./src/contexts/Auth.jsx
import { createContext } from "react";
export const Auth = createContext();
Poskytněte kontext ověření
Nyní potřebujeme odhalit kontextovou proměnnou "Auth", kterou jsme vytvořili dříve, abychom toho dosáhli, používáme poskytovatele kontextu, poskytovatel kontextu je komponenta, která má podpěru "hodnoty", tuto podpěru můžeme použít ke sdílení hodnoty – názvu – v rámci stromu komponent obalením stromu komponenty komponentou poskytovatele zpřístupníme tuto hodnotu odkudkoli uvnitř stromu komponenty, aniž bychom museli předávat tuto podpěru každé podřízené komponentě jednotlivě.
V našem příkladu autentizace potřebujeme proměnnou pro uložení objektu uživatele a nějaký druh setteru, který by s touto stavovou proměnnou manipuloval, toto je perfektní případ použití pro useState. Při používání Context se musíte ujistit, že definujete data, která chcete poskytnout, a předáváte tato data – v našem příkladu uživatel – všemu stromu komponenty vnořenému uvnitř, takže definujte nového uživatele stavové proměnné uvnitř komponenty poskytovatele a nakonec předáme uživatele i setUser uvnitř pole jako hodnotu, kterou poskytovatel vystaví stromu komponent:
./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>;
};
Další věc, kterou můžeme udělat, je přesunout naši stavovou proměnnou „name“ z hlavní komponenty aplikace do kontextu Auth a vystavit ji vnořeným komponentám:
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>;
};
Teď už zbývá jen vnořit naši aplikaci do stejné komponenty AuthProvider, kterou jsme právě exportovali.
./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>
);
Protože vykreslujeme podřízenou podpěru uvnitř Auth.Provider, všechny prvky, které jsou vnořeny do komponenty AuthProvider, nyní mohou využívat hodnotu podpěry, kterou jsme předali Auth.Provider. Může se to zdát matoucí, ale jakmile s tím budete experimentovat, zkuste poskytnout a konzumovat globální – kontext – stav. Ostatně to mi dávalo smysl až po experimentování s kontextovým API.
Použití kontextu ověření
Poslední krok je přímočarý, používáme kontextový hák „useContext“ pro přístup k hodnotě, kterou poskytuje poskytovatel kontextu „Auth“, což je v našem případě pole obsahující uživatele a setUser, v následujícím kódu jsme schopni použijte useContext k použití kontextu Auth uvnitř navigační lišty. To je možné pouze proto, že Navbar je vnořen do komponenty App, a protože AuthProvider obklopuje komponentu App, lze hodnotu prop spotřebovat pouze pomocí háku useContext. Další úžasný nástroj React poskytuje hned po vybalení pro správu jakýchkoli dat, ke kterým lze přistupovat globálně a také s nimi manipulovat jakákoli spotřební komponenta.
./src/components/Navbar.jsx
export default function Navbar() {
const [name, setName] = useContext(Auth);
console.log(name)
return (...)};
Všimněte si, že ve funkční komponentě Navbar() již nepřijímáme žádné rekvizity. Místo toho používáme useContext(Auth) ke konzumaci kontextu Auth, přičemž se chytí jak jméno, tak setName. To také znamená, že již nemusíme předávat rekvizitu Navbar:
./src/App.jsx
// ... //
return (
<BrowserRouter>
<div className="h-screen">
<Navbar/> // no need to pass prop anymore
// ... //
Aktualizace kontextu ověření
K manipulaci se stavovou proměnnou „name“ můžeme také použít poskytnutou funkci setName:
./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");
Představujeme háček useReducer().
V předchozím příkladu jsme ke správě a sdílení našeho stavu ve stromu komponent použili kontextové API, možná jste si všimli, že stále používáme useState jako základ naší logiky, a to je většinou v pořádku, protože náš stav je stále jednoduchý objekt. v tuto chvíli, ale pokud bychom chtěli rozšířit možnosti našeho ověřovacího toku, budeme určitě muset ukládat více než jen e-mail aktuálně přihlášeného uživatele, a zde se vrátíme zpět k omezením, do kterých jsme se dříve v souvislosti s použití useState s komplexním stavem naštěstí React řeší tento problém poskytnutím alternativy k useState pro správu komplexního stavu: zadejte useReducer.
useReducer lze považovat za zobecněnou verzi useState, vyžaduje dva parametry: funkci redukce a počáteční stav.
Pro počáteční stav není nic zajímavého, kouzla se odehrávají uvnitř funkce reduktoru: kontroluje typ akce, ke které došlo, a v závislosti na této akci reduktor určí, jaké aktualizace se mají na stav použít a vrátí svou novou hodnotu. .
Při pohledu do níže uvedeného kódu má funkce redukce dva možné typy akcí:
-
"LOGIN": v takovém případě se stav uživatele aktualizuje pomocí nových uživatelských pověření poskytnutých v datové části akce.
-
"LOGOUT": v takovém případě bude uživatelský stav odstraněn z místního úložiště a nastaven zpět na null.
Je důležité poznamenat, že objekt akce obsahuje jak pole typu, které určuje, jakou logiku použít, tak volitelné pole užitečného zatížení, které poskytuje data, která jsou nezbytná pro aplikaci této logiky.
Nakonec hák useReducer vrátí aktuální stav a funkci odeslání, kterou používáme k předání akce reduktoru.
Pro zbytek logiky je to totožné s předchozím příkladem:
./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>
);
};
Odesílání akcí namísto použití funkce setState setter – např.: setName –
Jak jsme právě zmínili, používáme funkci odeslání k předání akce reduktoru, v následujícím kódu zahájíme akci PŘIHLÁŠENÍ a poskytneme e-mail uživatele jako užitečné zatížení, nyní se stav uživatele aktualizuje a tato změna se spustí opětovné vykreslení všech komponent přihlášených do stavu uživatele. Je důležité zdůraznit, že opětovné vykreslení bude spuštěno pouze v případě, že dojde ke skutečné změně stavu, žádné opětovné vykreslení, pokud reduktor vrátí stejný předchozí stav.
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 (...)};
Uživatelské přihlášení
Profesionální tip
Všimněte si, jak je objekt uživatele, který obdržíme po úspěšném přihlášení, nyní uložen v localStorage.
Vlastní háček pro přihlášení
Nyní, když dobře ovládáme useReducer, můžeme dále zapouzdřit naši logiku přihlášení a odhlášení do jejich vlastních samostatných háčků, jediným voláním háčku Login, můžeme zpracovat volání API k trase přihlášení, získat nového uživatele. přihlašovací údaje a uložte je do místního úložiště, odešlete volání LOGIN pro aktualizaci stavu uživatele, a to vše při řešení chyb:
./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 };
};
Poznámka: pro pokročilejší uživatele Reactu z řad čtenářů by vás mohlo zajímat, proč jsme nepoužili funkci líné inicializace useReducer k načtení přihlašovacích údajů uživatele, useReducer akceptuje třetí volitelný parametr zvaný funkce init, tato funkce se používá v případě musíme použít nějakou logiku, než získáme počáteční hodnotu stavu, důvod, proč jsme se pro to nerozhodli, je jednoduchá záležitost oddělení zájmů, kód je tímto způsobem jednodušší na pochopení a v důsledku toho se snáze udržuje .
Přihlašovací stránka
Zde je návod, jak vypadá horní část naší přihlašovací stránky po použití háčku useLogin() k extrahování přihlašovací funkce a vyvolání přihlašovací funkce s přihlašovacími údaji zadanými uživatelem:
// ... ... //
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 (...)
// ... ... //
Také deaktivujeme funkci Odeslat, když uživatel odešle formulář:
<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>
A vykreslení jakýchkoli chyb ověřování, které obdržíme z našeho backendu:
{
error && <span className="text-red-500 p-2">{error.message}</span>;
}
Udržování stavu uživatele
Možná se divíte, proč potřebujeme ukládat objekt uživatele do localStorage, jednoduše řečeno, chceme nechat uživatele přihlášeného, dokud nevyprší platnost tokenu. Použití localStorage je skvělý způsob, jak ukládat bity JSON, stejně jako v našem příkladu. Všimněte si, jak se stav vymaže, pokud stránku po přihlášení obnovíte. To lze snadno vyřešit pomocí háčku useEffect, který zkontroluje, zda máme uložený objekt uživatele v localStorage, pokud existuje, přihlásíme uživatele automaticky:
// ... ... //
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>
);
};
Vlastní háčky pro odhlášení
Totéž platí pro Logout hook, zde odesíláme akci LOGOUT pro odstranění aktuálních uživatelských přihlašovacích údajů ze stavu i místního úložiště:
./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 };
};
Odhlášení uživatele
Chcete-li uživatele odhlásit, přidejte událost kliknutí k tlačítku Odhlášení, které se nachází v UserDropdown.jsx, a podle toho s ním naložte:
./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>
// ... ... //
Ochrana React Routes
Posledním krokem při implementaci naší aplikace je využití globálního stavu uživatele k ovládání navigace uživatele, rychlé připomenutí toho, jakého chování bychom měli dosáhnout: zpočátku je uživatel přivítán přihlašovací stránkou, od té chvíle má uživatel přístup pouze na domovskou stránku po úspěšném přihlášení bude obdobně uživatel po odhlášení přesměrován na přihlašovací stránku.
Toho dosáhneme pomocí knihovny Reag-router-dom tím, že definujeme 2 cesty: "/" a "/login", pomocí globálního stavu auth řídíme, která komponenta se má na každé cestě vykreslit., auth vyhodnocování na null představuje neověřeného uživatele a naopak:
./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;
Rekapitulační schéma
Zabalit
V tomto článku jsme se pokusili vyřešit jednoduchý, ale velmi běžný případ použití implementace správy stavu pro pracovní postup ověřování, prošli jsme různými přístupy, zdůvodněním každého z nich a jejich kompromisy. Správa stavu v klientských frameworkech a zejména v Reactu je jedním z nejdiskutovanějších témat v komunitě frontendu, jednoduše proto, že může buď zvýšit nebo narušit výkon a škálovatelnost vaší aplikace. Obrovské množství různých technik, vzorů, knihoven a nástrojů, které se snaží vyřešit tento problém státní správy, je ohromující, naším cílem bylo poskytnout vám solidní pochopení postupů, abyste je mohli implementovat do své vlastní aplikace pro pokročilejší techniky a vzory správy stavu ve složitějších aplikacích, podívejte se na náš další článek, kde se dostaneme do reduxu pro škálovatelné řízení stavu v Reactu.
Již brzy
ContextAPI vs Redux Toolkit
Redux byl v komunitě přijat mnoho let před sadou nástrojů Redux, ve skutečnosti byl RTK představen jako startovací šablona pro bootstrap správu stavu redux v nových aplikacích (jeho původní název byl „redux-starter-kit“ v říjnu 2019), i když dnes existuje všeobecná shoda mezi správci Reduxu a komunitou, že sada nástrojů Redux je platným způsobem práce s reduxem. RTX abstrahuje mnoho z logiky Redux, což usnadňuje použití, je mnohem méně upovídané, a povzbuzuje vývojáře, aby dodržujte osvědčené postupy, jedním z hlavních rozdílů mezi těmito dvěma je to, že Redux byl postaven tak, aby nebyl názorem, poskytoval minimální API a očekával, že vývojáři udělají většinu těžké práce psaním vlastních knihoven pro běžné úkoly a zabývají se strukturou kódu, to vedlo k pomalému vývoji a chaotickému kódu, sada nástrojů Redux byla přidána jako další vrstva abstrakce, která vývojářům brání v tom, aby se dostali do jejích běžných nástrah, další informace a úvahy od jejích správců najdete v oficiální dokumentaci zde.