React je ogrodje za gradnjo dinamičnih aplikacij na strani odjemalca za mnoge razvijalce. Dinamična narava teh aplikacij izhaja iz prilagodljivosti in razširjenega seznama zmožnosti in funkcij, ki so na voljo na strani odjemalca, kar je razvijalcem omogočilo, da so v nekaj sekundah zgradili popolnoma razvite aplikacije, ki se naložijo v brskalnik, kar je podvig, ki ni bil mogoče (ali zelo okorno) v dneh statičnega spleta.
S to razširitvijo možnosti se je pojavil koncept upravljanja stanja, ko se kompleksnost aplikacij na strani odjemalca povečuje, potreba po ohranjanju lokalnega stanja raste in postane samo po sebi ozko grlo, če se z njim ne ravna pravilno in ni zasnovano z mislijo na razširljivost.
To vprašanje so obravnavali številni okviri, ki so sledili različnim pristopom in se osredotočali na različne nize podproblemov, zato je pomembno, da imamo visoko raven razumevanja ekosistema izbranega okvira, da ocenimo potrebe vsake aplikacije in uporabimo pravi pristop, ki sledi tem. meritve. Ta članek vam bo podal kratek pregled pogostih težav pri upravljanju stanja in poskusil predstaviti različne pristope (useState, Context API) kot odgovor nanje. Čeprav bo ta članek predstavil več rešitev, se bo osredotočil le na izzive v manjšem obsegu, v prihodnjih člankih pa bomo obravnavali bolj napredne teme.
Potek dela za preverjanje pristnosti
Kodo, prikazano v celotnem članku, lahko najdete tukaj.
Do povezave za predogled v živo lahko dostopate tukaj.
Razmislite o primeru, ko izvajamo postopek preverjanja pristnosti za aplikacijo React.
Kot je prikazano v zgornjem GIF-u, želimo uporabnikom omogočiti prijavo ali vpis v našo aplikacijo s poverilnicami. Če so bile podane veljavne poverilnice, bo uporabnik prijavljen, aplikacija se bo samodejno pomaknila na domačo stran in uporabnik lahko nadaljuje z uporabo aplikacije.
Podobno, če se uporabnik odjavi, bodo viri domače strani zaščiteni za prijavo, prijavna stran bo edina stran, do katere bo uporabnik dostopal.
Če razmišljamo o tem poteku dela v smislu implementacije, bi imeli glavno komponento z imenom App, komponenta App bo uporabnika preusmerila na eno od dveh strani: domačo stran ali prijavo, trenutno stanje uporabnika (prijavljen, odjavljen) bo narekovalo, katera strani, na katero je uporabnik preusmerjen, mora sprememba trenutnega stanja uporabnika (na primer sprememba iz prijavljenega v odjavljenega) sprožiti takojšnjo preusmeritev na ustrezno stran.
Kot je prikazano na zgornji ilustraciji, želimo, da komponenta aplikacije upošteva trenutno stanje in upodablja samo eno od dveh strani – domačo ali prijavno – na podlagi tega trenutnega stanja.
Če je uporabnik null, to pomeni, da nimamo nobenega overjenega uporabnika, zato se samodejno pomaknemo na stran za prijavo in zaščitimo domačo stran s pogojnim upodabljanjem. Če uporabnik obstaja, naredimo obratno.
Zdaj, ko dobro razumemo, kaj bi bilo treba implementirati, raziščimo nekaj možnosti, a najprej nastavimo naš projekt React,
Repo projekta vsebuje vzorčno zaledno aplikacijo, ki jo bomo uporabili za implementacijo sprednje strani (ne bomo se spuščali vanjo, ker tukaj ni glavni poudarek, vendar je bila koda namenoma preprosta, tako da z njo ne boste imeli težav )
Začnemo z ustvarjanjem naslednjih strani in komponent:
-
Domača stran
-
Stran za prijavo
-
stran EditUser
-
Komponenta Navbar
-
Komponenta UserDropdown
Aplikacija React z več stranmi potrebuje ustrezno navigacijo, za to lahko uporabimo react-router-dom za ustvarjanje globalnega konteksta usmerjevalnika brskalnika in registracijo različnih poti React.
yarn add react-router-dom
Vemo, da veliko bralcev raje sledi skupaj z vadnicami, zato je tukaj začetna predloga, s katero se boste lažje seznanili. Ta začetna veja uporablja DaisyUI za vnaprej določene komponente JSX TailwindCSS. Vključuje vse komponente, strani in že nastavljen usmerjevalnik. Če razmišljate o tem, da bi sledili tej vadnici in sami zgradili celoten potek avtorizacije po enostavnih korakih, začnite tako, da najprej razcepite repozitorij. Ko razcepite repozitorij, ga klonirajte in začnite iz start-hereveje:
git clone git@github.com:<your-username>/fullstack-resourcify.git
Ko povlečete vejo začetek tukaj:
-
Odprite projekt z želenim urejevalnikom kode
-
Spremenite imenik v frontend/
-
Namestitev odvisnosti: preja
-
Zaženite razvojni strežnik: yarn dev·
Predogled bi moral izgledati nekako takole:
Ime, upodobljeno v vrstici Navbar – zgoraj desno – je spremenljivka stanja, definirana v glavni komponenti aplikacije. Ista spremenljivka se posreduje vrstici Navbar in domači strani. Zgoraj uporabljen preprost obrazec dejansko posodobi spremenljivko stanja »ime« iz komponente EditPage.
Spodaj predstavljena pristopa bosta obravnavala podrobnosti izvajanja:
Prvi pristop: useState
useState()
useState je eden najpogosteje uporabljenih kavljev React, omogoča vam ustvarjanje in spreminjanje stanja v funkcijski komponenti React. Komponenta useState ima resnično preprosto izvedbo in je enostavna za uporabo: če želite ustvariti novo stanje, morate poklicati useState z začetno vrednostjo vašega stanja in kavelj useState bo vrnil matriko, ki vsebuje dve spremenljivki: prva je stanje spremenljivka, ki jo lahko uporabite za referenco svojega stanja, druga pa je funkcija, ki jo uporabite za spreminjanje vrednosti stanja: precej preprosto.
Kaj pa, če to vidimo v akciji? Ime, upodobljeno v vrstici Navbar – zgoraj desno – je spremenljivka stanja, definirana v glavni komponenti aplikacije. Ista spremenljivka se posreduje vrstici Navbar in domači strani. Zgoraj uporabljen preprost obrazec dejansko posodobi spremenljivko stanja »ime« iz komponente EditPage. Bistvo je naslednje: useState je jedrni kavelj, ki sprejme začetno stanje kot parameter in vrne dve spremenljivki z dvema vrednostima, spremenljivko stanja, ki vsebuje začetno stanje, in nastavitveno funkcijo za isto spremenljivko stanja.
Razčlenimo ga in poglejmo, kako je bilo to sploh implementirano.
- Ustvarjanje spremenljivke stanja »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 (...)};
Rekviziti
Rekvizite so eden od osnovnih gradnikov react komponente, konceptualno, če si predstavljate funkcionalno komponento React kot funkcijo Javascript, potem rekviziti niso nič več kot funkcijski parametri, kombinacija rekvizitov in kavelj useState vam lahko ponudi trden okvir za upravljanje stanja v preprosti aplikaciji React.
React props se kot atributi posredujejo komponentam po meri. Atribute, posredovane kot props, je mogoče destrukturirati iz objekta props, ko ga sprejmete kot argument, podobno temu:
Podajanje rekvizitov
<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>
Rekviziti se lahko sprejmejo in uporabljajo znotraj funkcionalne komponente, podobno kot običajni argumenti funkcije. Ker je »ime« posredovano kot podpora komponenti Home, ga lahko upodobimo v isti komponenti. V naslednjem primeru sprejemamo posredovani rekvizit s sintakso destrukturiranja, da ekstrahiramo lastnost imena iz objekta rekvizitov.
Sprejemanje rekvizitov
./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>
... ...
Profesionalni nasvet
Odprite konzolo brskalnika in opazite, kako se vse komponente, ki uporabljajo element »name«, ponovno upodabljajo, ko se stanje spremeni. Ko manipulirate s spremenljivko stanja, bo React shranil naslednje stanje, ponovno upodobil vašo komponento z novimi vrednostmi in posodobil uporabniški vmesnik.
useState Slabosti
Vrtanje rekvizitov
Vrtanje rekvizitov je izraz, ki se nanaša na hierarhijo komponent, kjer nabor komponent potrebuje določene rekvizite, ki jih zagotovi nadrejena komponenta. Običajna rešitev, ki jo običajno uporabljajo neizkušeni razvijalci, je, da te rekvizite posredujejo skozi celotno verigo komponent, težava s tem pristop je, da bo sprememba katerega koli od teh rekvizitov sprožila ponovno upodabljanje celotne verige komponent, kar bo učinkovito upočasnilo celotno aplikacijo zaradi teh nepotrebnih upodobitev, komponent na sredini verige, ki teh rekvizitov ne potrebujejo delujejo kot mediji za prenos rekvizitov.
Primer:
-
Spremenljivka stanja je bila definirana v glavni komponenti aplikacije s kavljem useState().
-
Podpora za ime je bila posredovana komponenti Navbar
-
Isti rekvizit, sprejet v vrstici Navbar in ponovno posredovan kot rekvizit komponenti UserDropdown
-
UserDropdown je zadnji podrejeni element, ki sprejme 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>
</>
);
}
Predstavljajte si, kako težko je vzdrževati aplikacijo React z različnimi plastmi komponent, ki vse prenašajo in upodabljajo isto stanje.
Naraščajoča kompleksnost in kakovost kode
Z uporabo useState in props kot edinega sredstva za upravljanje stanja v aplikaciji React lahko kodna baza hitro postane kompleksnejša, saj mora upravljati na desetine ali stotine spremenljivk stanja, ki so lahko dvojniki druga druge, razpršene po različnih datotekah in komponente so lahko precej zastrašujoče, bo vsaka sprememba dane spremenljivke stanja zahtevala natančno preučitev odvisnosti med komponentami, da bi se izognili morebitnemu dodatnemu ponovnemu upodabljanju v že tako počasni aplikaciji.
Drugi pristop: Kontekstni API
Kontekstni API je poskus Reacta rešiti pomanjkljivosti samo uporabe rekvizitov in useState za upravljanje stanja, še posebej, kontekstni API je odgovor na prej omenjeno težavo v zvezi s potrebo po posredovanju rekvizitov navzdol po celotnem drevesu komponent. Z uporabo konteksta lahko definirate stanje za podatke, za katere menite, da so globalni, in dostopate do njihovega stanja s katere koli točke v drevesu komponent: nič več vrtanja podpornikov.
Pomembno je poudariti, da je bil kontekstni API prvotno zasnovan za reševanje vprašanja globalne skupne rabe podatkov, podatkov, kot so teme uporabniškega vmesnika, informacije o preverjanju pristnosti, ki so naš primer uporabe, jeziki in podobno), za druge vrste podatkov, ki jih je treba deliti med več kot eno komponento, vendar ni nujno globalen za vso aplikacijo, kontekst morda ni najboljša možnost, odvisno od primera uporabe, lahko razmislite o drugih tehnikah, kot je komponentna sestava, kar je izven obsega tega članka.
Preklop na kontekst
Ustvarjanje avtentičnega konteksta
createContext je preprost, ustvari novo kontekstno spremenljivko, sprejme en sam izbirni parameter: privzeto vrednost kontekstne spremenljivke.
Oglejmo si to v akciji, najprej ustvarite novo mapo »contexts« in v njej novo datoteko »Auth.jsx«. Če želite ustvariti nov kontekst, moramo poklicati funkcijo createContext(), vrnjeno vrednost dodeliti novi spremenljivki Auth, ki bo naslednja izvožena:
./src/contexts/Auth.jsx
import { createContext } from "react";
export const Auth = createContext();
Zagotovite kontekst za preverjanje avtorizacije
Zdaj moramo izpostaviti spremenljivko konteksta "Auth", ki smo jo ustvarili prej, da to dosežemo, uporabimo ponudnika konteksta, ponudnik konteksta je komponenta, ki ima prop "value", ta prop lahko uporabimo za skupno rabo vrednosti – ime – v celotnem drevesu komponent, z ovijanjem drevesa komponent s komponento ponudnika naredimo to vrednost dostopno od koder koli znotraj drevesa komponent, ne da bi bilo treba ta prop posredovati vsaki podrejeni komponenti posebej.
V našem primeru preverjanja pristnosti potrebujemo spremenljivko, ki zadrži uporabniški objekt, in nekakšen nastavitelj, ki manipulira s to spremenljivko stanja, to je popoln primer uporabe za useState. Ko uporabljate kontekst, se morate prepričati, da definirate podatke, ki jih želite zagotoviti, in posredujete te podatke – uporabnika v našem primeru – celotnemu drevesu komponent, ki je ugnezdeno znotraj, zato definirajte novo spremenljivko stanja uporabnik znotraj komponente ponudnika in končno podamo uporabnika in setUser znotraj matrike kot vrednost, ki jo bo ponudnik izpostavil drevesu 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>;
};
Druga stvar, ki jo lahko naredimo, je, da premaknemo našo spremenljivko stanja »ime« iz glavne komponente aplikacije v kontekst Auth in jo izpostavimo ugnezdenim komponentam:
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>;
};
Zdaj je vse, kar ostane, ugnezditi našo aplikacijo v isto komponento AuthProvider, ki smo jo pravkar izvozili.
./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>
);
Ker upodabljamo otroški prop znotraj Auth.Provider, lahko vsi elementi, ki so ugnezdeni znotraj komponente AuthProvider, zdaj uporabljajo vrednost prop, ki smo jo posredovali Auth.Provider. Morda se zdi zmedeno, toda ko enkrat eksperimentirate s tem, poskusite zagotoviti in porabiti globalno - Kontekst - stanje. Navsezadnje se mi je to zdelo smiselno šele po eksperimentiranju s Context API-jem.
Uporaba konteksta za preverjanje avtorizacije
Zadnji korak je preprost, uporabimo kontekstni kavelj "useContext" za dostop do vrednosti, ki jo zagotavlja kontekstni ponudnik "Auth", ki je v našem primeru matrika, ki vsebuje uporabnika in setUser, v naslednji kodi lahko uporabite useContext za uporabo konteksta Auth znotraj vrstice Navbar. To je mogoče samo zato, ker je vrstica Navbar ugnezdena znotraj komponente aplikacije in ker se AuthProvider ovije okoli komponente aplikacije, je mogoče uporabiti prop vrednosti samo s kavljem useContext. Še eno izjemno orodje, ki ga React ponuja že takoj za upravljanje vseh podatkov, do katerih je mogoče dostopati globalno in z njimi manipulira katera koli potrošniška komponenta.
./src/components/Navbar.jsx
export default function Navbar() {
const [name, setName] = useContext(Auth);
console.log(name)
return (...)};
Upoštevajte, da v funkcionalni komponenti Navbar() ne sprejemamo nobenih rekvizitov. Namesto tega uporabljamo useContext(Auth), da uporabimo kontekst Auth, pri čemer zajamemo ime in setName. To tudi pomeni, da nam ni treba več posredovati prop v Navbar:
./src/App.jsx
// ... //
return (
<BrowserRouter>
<div className="h-screen">
<Navbar/> // no need to pass prop anymore
// ... //
Posodabljanje konteksta preverjanja
Priloženo funkcijo setName lahko uporabimo tudi za manipulacijo spremenljivke stanja »name«:
./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");
Predstavljamo kavelj useReducer().
V prejšnjem primeru smo uporabili kontekstni API za upravljanje in skupno rabo našega stanja navzdol po drevesu komponent, morda ste opazili, da še vedno uporabljamo useState kot osnovo naše logike, in to je večinoma v redu, saj je naše stanje še vedno preprost objekt na tej točki, vendar če bi želeli razširiti zmogljivosti našega toka preverjanja pristnosti, bomo zagotovo morali shraniti več kot le e-pošto uporabnika, ki je trenutno prijavljen, in tu se vrnemo nazaj k omejitvam, ki smo jih imeli prej v zvezi z uporaba useState s kompleksnim stanjem, na srečo React reši to težavo tako, da ponudi alternativo useState za upravljanje kompleksnega stanja: vnesite useReducer.
useReducer si lahko predstavljamo kot posplošeno različico useState, saj ima dva parametra: funkcijo reduktorja in začetno stanje.
Za začetno stanje ni nič zanimivega, čarovnija se zgodi znotraj funkcije reduktorja: preveri vrsto dejanja, ki se je zgodilo, in glede na to dejanje bo reduktor določil, katere posodobitve uporabiti za stanje, in vrnil njegovo novo vrednost .
Če pogledamo spodnjo kodo, ima funkcija reduktorja dve možni vrsti dejanj:
-
"PRIJAVA": v tem primeru bo uporabniško stanje posodobljeno z novimi uporabniškimi poverilnicami, ki so podane v tovoru dejanja.
-
"ODJAVA": v tem primeru bo uporabniško stanje odstranjeno iz lokalnega pomnilnika in nastavljeno nazaj na nič.
Pomembno je omeniti, da objekt dejanja vsebuje polje vrste, ki določa, katero logiko je treba uporabiti, in izbirno polje koristne obremenitve, ki zagotavlja podatke, potrebne za uporabo te logike.
Končno kljuka useReducer vrne trenutno stanje in dispetch funkcijo, ki jo uporabimo za posredovanje dejanja reduktorju.
Za ostalo logiko je identičen prejšnjemu primeru:
./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>
);
};
Pošiljanje dejanj namesto uporabe nastavitvene funkcije setState – npr.: setName –
Kot smo pravkar omenili, uporabljamo funkcijo odpošiljanja za posredovanje dejanja reduktorju, v naslednji kodi sprožimo dejanje PRIJAVE in zagotovimo uporabniško e-pošto kot obremenitev, zdaj se bo uporabniško stanje posodobilo in ta sprememba bo sprožila ponovno upodabljanje vseh komponent, naročenih na uporabniško stanje. Pomembno je poudariti, da se bo ponovno upodabljanje sprožilo le, če pride do dejanske spremembe stanja, brez ponovnega upodabljanja, če reduktor vrne isto prejšnje stanje.
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 (...)};
Prijava uporabnika
Profesionalni nasvet
Opazite, kako je uporabniški objekt, ki ga prejmemo po uspešni prijavi, zdaj shranjen v localStorage.
Kavelj po meri za prijavo
Zdaj, ko dobro obvladamo useReducer, lahko našo logiko prijave in odjave dodatno zajamemo v njihove lastne ločene kljuke po meri, z enim samim klicem kljuke Login lahko obravnavamo klic API-ja na prijavno pot, pridobimo novega uporabnika poverilnice in jih shranite v lokalno shrambo, pošljite klic LOGIN za posodobitev stanja uporabnika, pri čemer se ukvarjate z obravnavanjem napak:
./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 };
};
Opomba: za naprednejše uporabnike Reacta med bralci se morda sprašujete, zakaj nismo uporabili lene inicializacijske funkcije useReducer za pridobitev uporabniških poverilnic, useReducer sprejme tretji izbirni parameter, imenovan funkcija init, ta funkcija se uporablja v primeru moramo uporabiti nekaj logike, preden lahko dobimo začetno vrednost stanja, razlog, da se za to nismo odločili, je preprosta stvar ločitve pomislekov, koda na ta način je preprostejša za razumevanje in posledično preprostejša za vzdrževanje .
Stran za prijavo
Takole je videti zgornji del naše strani za prijavo po uporabi kljuke useLogin() za ekstrahiranje funkcije za prijavo in priklicu funkcije za prijavo s poverilnicami, ki jih je poslal uporabnik:
// ... ... //
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 (...)
// ... ... //
Prav tako onemogočimo funkcijo Pošlji, ko uporabnik odda obrazec:
<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>
In upodabljanje morebitnih napak pri preverjanju pristnosti, ki jih prejmemo iz našega zaledja:
{
error && <span className="text-red-500 p-2">{error.message}</span>;
}
Ohranjanje uporabniškega stanja
Morda se sprašujete, zakaj moramo uporabniški objekt shraniti v localStorage, preprosto povedano, uporabnika želimo obdržati prijavljenega, dokler žeton ne poteče. Uporaba localStorage je odličen način za shranjevanje bitov JSON, tako kot v našem primeru. Opazite, kako se stanje izbriše, če po prijavi osvežite stran. To je mogoče enostavno rešiti s kavljem useEffect, da preverimo, ali imamo shranjen uporabniški objekt v localStorage; če obstaja, uporabnika samodejno prijavimo:
// ... ... //
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>
);
};
Kavlji po meri za odjavo
Enako velja za kavelj za odjavo, tukaj pošiljamo dejanje ODJAVE, da odstranimo poverilnice trenutnega uporabnika iz stanja in lokalnega pomnilnika:
./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 };
};
Odjava uporabnika
Če želite odjaviti uporabnika, dodamo dogodek klika gumbu Odjava, ki ga najdete v UserDropdown.jsx, in ga ustrezno obravnavamo:
./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>
// ... ... //
Zaščita poti React
Zadnji korak pri izvajanju naše aplikacije je izkoriščanje globalnega uporabniškega stanja za nadzor uporabniške navigacije, hiter opomnik o tem, kakšno vedenje moramo doseči: na začetku uporabnika pozdravi stran za prijavo, od te točke lahko uporabnik dostopa le do domače strani po uspešni prijavi, podobno bo uporabnik ob odjavi preusmerjen na stran za prijavo.
To dosežemo s pomočjo knjižnice react-router-dom z definiranjem 2 poti: "/" in "/login", nadzorujemo, katero komponento naj upodabljamo na vsaki poti z uporabo globalnega stanja avtorizacije, vrednotenje auth na ničelno predstavlja nepreverjenega uporabnika in obratno:
./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;
Povzetek diagrama
Zaviti
V tem članku smo poskušali obravnavati preprost, a zelo pogost primer uporabe upravljanja stanja za delovni tok preverjanja pristnosti, pri čemer smo šli skozi različne pristope, razloge za vsakega od njih in njihove kompromise. Upravljanje stanja v ogrodjih na strani odjemalca in zlasti v Reactu je ena izmed tem, o kateri se največ govori v skupnosti frontenda, preprosto zato, ker lahko izboljša ali poruši zmogljivost in razširljivost vaše aplikacije. Sama količina različnih tehnik, vzorcev, knjižnic in orodij, ki poskušajo rešiti to težavo upravljanja stanja, je ogromna, naš cilj je bil, da vam zagotovimo dobro razumevanje praks, da jih lahko implementirate v svojo aplikacijo za naprednejše tehnike in vzorce upravljanja stanja v bolj zapletenih aplikacijah, si oglejte naš naslednji članek, kjer se poglobimo v redux za razširljivo upravljanje stanja v Reactu.
Prihaja kmalu
ContextAPI proti Redux Toolkit
Redux je bil v skupnosti že vrsto let sprejet pred kompletom orodij Redux, pravzaprav je bil RTK uveden kot začetna predloga za zagon upravljanja stanja redux v novih aplikacijah (njegovo začetno ime je bilo »redux-starter-kit« oktobra 2019), čeprav danes obstaja splošno soglasje med vzdrževalci Reduxa in skupnostjo, da je komplet orodij Redux veljaven način za delo z reduxom.RTX abstrahira veliko logike Reduxa, kar olajša uporabo, z veliko manj besedami, in spodbuja razvijalce k sledite najboljšim praksam, ena glavna razlika med obema je ta, da je bil Redux zgrajen tako, da je brez mnenja, zagotavlja minimalen API in pričakuje, da bodo razvijalci večino težkega dela opravili s pisanjem lastnih knjižnic za običajna opravila in obravnavanjem strukture kode, to je povzročilo počasen razvojni čas in neurejeno kodo, komplet orodij Redux je bil dodan kot dodatna plast abstrakcije, ki je razvijalcem preprečila, da bi se znašli v njegovih običajnih pasteh, glejte uradno dokumentacijo za več vpogledov in utemeljitev njegovih vzdrževalcev tukaj.