React er det viktigste rammeverket for å bygge dynamiske klientsideapplikasjoner for mange utviklere. Den dynamiske naturen til disse applikasjonene kommer fra fleksibiliteten og den utvidede listen over muligheter og funksjoner som er mulig på klientsiden som gjorde det mulig for utviklere å bygge fullverdige applikasjoner som lastes inn i nettleseren i løpet av sekunder, en prestasjon som ikke var mulig (eller svært tungvint) i den statiske nettets dager.
Med denne utvidelsen av mulighetene, kom konseptet med å administrere staten, ettersom kompleksiteten vokser i applikasjoner på klientsiden, vokser behovet for å beholde den lokale staten til å bli en flaskehals i seg selv hvis den ikke håndteres riktig og unnfanges med skalerbarhet i tankene.
Dette problemet ble adressert av mange rammeverk, etter forskjellige tilnærminger og med fokus på forskjellige sett med delproblemer, det er derfor det er viktig å ha en forståelse på høyt nivå av økosystemet til det valgte rammeverket for å vurdere hver applikasjons behov og bruke den riktige tilnærmingen etter disse. beregninger. Denne artikkelen vil gi deg en kort oversikt over de vanlige tilstandsstyringsproblemene og prøve å introdusere forskjellige tilnærminger (useState, Context API) som et svar på det. Selv om denne artikkelen vil presentere flere løsninger, vil den kun fokusere på utfordringer i mindre skala, vi vil dekke mer avanserte emner i kommende artikler.
Arbeidsflyt for autentisering
Koden som vises gjennom artikkelen finner du her.
En direkte forhåndsvisningslink kan nås her.
Vurder tilfellet der vi implementerer autentiseringsprosessen for en React-applikasjon.
Som vist i GIF-en ovenfor, ønsker vi å la brukere logge på eller registrere seg for applikasjonen vår ved å bruke legitimasjon. Hvis gyldig legitimasjon ble oppgitt, vil brukeren logges på, applikasjonen vil automatisk navigere til hjemmesiden og brukeren kan fortsette å bruke applikasjonen.
Tilsvarende, hvis brukeren logger ut, vil hjemmesideressursene være beskyttet bak pålogging, påloggingssiden vil være den eneste siden som er tilgjengelig for brukeren.
Når vi tenker på denne arbeidsflyten når det gjelder implementering, vil vi ha en hovedkomponent som heter App, App-komponenten vil omdirigere brukeren til en av to sider: Hjem eller Logg inn, brukerens nåværende tilstand (pålogget, utlogget) vil diktere hvilken siden brukeren blir omdirigert til, bør en endring i brukerens nåværende tilstand (for eksempel endring fra pålogget til utlogget) utløse en umiddelbar omdirigering til den tilsvarende siden.
Som vist i illustrasjonen ovenfor, ønsker vi at app-komponenten skal vurdere gjeldende tilstand og bare gjengi én av de to sidene – Hjem eller Logg inn – basert på den gjeldende tilstanden.
Hvis brukeren er null, betyr det at vi ikke har noen autentisert bruker, så vi navigerer automatisk til påloggingssiden og beskytter hjemmesiden med betinget gjengivelse. Hvis brukeren eksisterer, gjør vi det motsatte.
Nå som vi har en solid forståelse av hva som bør implementeres, la oss utforske noen få alternativer, men la oss først sette opp React-prosjektet vårt,
Prosjektrepoen inneholder et eksempel på en backend-applikasjon som vi vil bruke til å implementere frontend-siden (vi vil ikke gå inn på det siden det ikke er hovedfokuset her, men koden ble med vilje holdt enkel, så du vil ikke ha det vanskelig med det )
Vi starter med å lage følgende sider og komponenter:
-
Hjemmeside
-
Påloggingsside
-
Rediger brukerside
-
Navbar-komponent
-
UserDropdown-komponent
En React-applikasjon med flere sider trenger riktig navigasjon, for det kan vi bruke react-router-domen til å lage en global nettleserruterkontekst og registrere forskjellige React-ruter.
yarn add react-router-dom
Vi vet at mange lesere foretrekker å følge med i veiledninger, så her er startmalen for å få deg oppdatert. Denne startgrenen bruker DaisyUI for forhåndsdefinerte TailwindCSS JSX-komponenter. Den inkluderer alle komponenter, sider og ruteren som allerede er konfigurert. Hvis du vurderer å følge denne opplæringen, bygge hele autentiseringsflyten selv ved å følge enkle trinn, start med å forgrene depotet først. Etter at du deler depotet, kloner du det og starter fra start-here-grenen**:**
git clone git@github.com:<your-username>/fullstack-resourcify.git
Når du trekker start-her-grenen:
-
Åpne prosjektet med din foretrukne kodeeditor
-
Endre katalog til frontend/
-
Installasjonsavhengigheter: garn
-
Start en utviklingsserver: yarn dev·
Forhåndsvisningen skal se omtrent slik ut:
Navnet som er gjengitt i navigasjonslinjen – øverst til høyre – er en tilstandsvariabel definert i hovedapp-komponenten. Den samme variabelen sendes til både Navbar og Hjemmeside. Det enkle skjemaet som brukes ovenfor oppdaterer faktisk tilstandsvariabelen "navn" fra EditPage-komponenten.
De to tilnærmingene som presenteres nedenfor vil gå inn på detaljene for implementering:
Første tilnærming: useState
useState()
useState er en av de mest brukte React-krokene, den lar deg opprette og mutere tilstand i en React Functional-komponent. UseState-komponenten har en veldig enkel implementering og er enkel å bruke: for å opprette en ny tilstand, må du kalle useState med startverdien til tilstanden din, og useState-kroken vil returnere en matrise som inneholder to variabler: den første er tilstanden variabel som du kan bruke for å referere til tilstanden din, og den andre en funksjon som du bruker til å endre verdien av staten: ganske grei.
Hva med å se det i aksjon? Navnet som er gjengitt i navigasjonslinjen – øverst til høyre – er en tilstandsvariabel definert i hovedapp-komponenten. Den samme variabelen sendes til både Navbar og Hjemmeside. Det enkle skjemaet som brukes ovenfor oppdaterer faktisk tilstandsvariabelen "navn" fra EditPage-komponenten. Bunnlinjen er dette: useState er en kjernekrok som aksepterer en starttilstand som en parameter og returnerer to variabler som har to verdier, tilstandsvariabelen som inneholder starttilstanden og en setterfunksjon for samme tilstandsvariabel.
La oss bryte det ned og se hvordan det ble implementert i utgangspunktet.
- Opprette tilstandsvariabelen «navn»:
./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 (...)};
Rekvisitter
Rekvisitter er en av de grunnleggende byggesteinene til en react-komponent, konseptuelt, hvis du tenker på en React-funksjonell komponent som en Javascript-funksjon, så er ikke rekvisitter mer enn funksjonsparametrene, og å kombinere rekvisitter og useState-kroken kan tilby deg et solid rammeverk for å administrere tilstand på tvers av en enkel React-applikasjon.
React-rekvisitter sendes som attributter til tilpassede komponenter. Attributter som sendes som rekvisitter kan destruktureres fra rekvisittobjektet når man aksepterer det som et argument, lik dette:
Passerende rekvisitter
<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>
Rekvisitter kan aksepteres og brukes i en funksjonell komponent som ligner på vanlige funksjonsargumenter. Det er fordi "navn" sendes som en rekvisitt til Home-komponenten, at vi kan gjengi den i samme komponent. I det følgende eksemplet godtar vi den beståtte rekvisitten ved å bruke destruktureringssyntaksen for å trekke ut navnegenskapen fra props-objektet.
Godtar rekvisitter
./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>
... ...
Profftips
Åpne nettleserens konsoll, og legg merke til hvordan alle komponenter som bruker "navn"-rekvisitten gjengis på nytt når tilstanden endres. Når du manipulerer en tilstandsvariabel, vil React lagre den neste tilstanden, gjengi komponenten din igjen med de nye verdiene og oppdatere brukergrensesnittet.
useState ulemper
Rekvisitter-Drilling
Rekvisittboring er et begrep for å referere til et hierarki av komponenter der et sett med komponenter trenger visse rekvisitter levert av en overordnet komponent, en vanlig løsning som uerfarne utviklere vanligvis bruker er å sende disse rekvisittene gjennom hele kjeden av komponenter, problemet med dette tilnærmingen er at en endring i noen av disse rekvisittene vil trigge hele kjeden av komponenter til å gjengi, og reduserer effektivt hele applikasjonen som et resultat av disse unødvendige gjengivelsene, komponentene i midten av kjeden som ikke krever disse rekvisittene fungere som medium for overføring av rekvisitter.
Eksempel:
-
En tilstandsvariabel ble definert i app-hovedkomponenten ved å bruke useState()-kroken
-
En navnrekvisitt ble sendt til Navbar-komponenten
-
Samme rekvisitt akseptert i Navbar og sendt som rekvisitt igjen til UserDropdown-komponenten
-
UserDropdown er det siste underordnede elementet som godtar rekvisitten.
./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>
</>
);
}
Tenk deg hvor vanskelig det er å opprettholde en React-app med forskjellige lag med komponenter som alle passerer og gjengir den samme tilstanden.
Økende kompleksitet og kodekvalitet
Med bruk av useState og rekvisitter som det eneste middelet for tilstandsstyring i en react-applikasjon, kan kodebasen raskt vokse i kompleksitet, og må administrere titalls eller hundrevis av tilstandsvariabler, som kan være duplikater av hverandre, spredt over forskjellige filer og komponenter kan være ganske skremmende, enhver endring av en gitt tilstandsvariabel vil kreve nøye vurdering av avhengigheter mellom komponentene for å unngå eventuell ytterligere gjengivelse i en allerede treg applikasjon.
Second Approach: Context API
Context API er Reacts forsøk på å løse ulempene med å kun bruke rekvisitter og useState for statsadministrasjon, spesielt kontekst API kommer som et svar på problemet tidligere nevnt angående behovet for å sende rekvisitter ned hele komponenttreet. Med bruk av kontekst kan du definere en tilstand for data som du anser som globale og få tilgang til dens tilstand fra et hvilket som helst punkt i komponenttreet: ikke mer propellboring.
Det er viktig å påpeke at kontekst-API-en opprinnelig ble skapt for å løse problemet med global datadeling, data som brukergrensesnitt-temaer, autentiseringsinformasjon som er vårt bruksområde, språk og slikt), for andre typer data som må deles blant mer enn én komponent, men er ikke nødvendigvis global for alle applikasjonene, er kontekst kanskje ikke det beste alternativet, avhengig av brukstilfellet kan du vurdere andre teknikker som komponentsammensetning som er utenfor rammen av denne artikkelen.
Bytter til kontekst
Opprette Auth-konteksten
createContext er enkel, den skaper en ny kontekstvariabel, den tar inn en enkelt valgfri parameter: standardverdien til kontekstvariabelen.
La oss se dette i aksjon, først lag en ny mappe "kontekster" og en ny fil inne i den "Auth.jsx". For å lage en ny kontekst, må vi påkalle createContext()-funksjonen, tilordne den returnerte verdien til en ny variabel Auth som vil bli eksportert neste gang:
./src/contexts/Auth.jsx
import { createContext } from "react";
export const Auth = createContext();
Oppgi godkjenningskonteksten
Nå må vi eksponere "Auth" kontekstvariabelen som vi opprettet tidligere, for å oppnå dette bruker vi kontekstleverandøren, kontekstleverandøren er en komponent som har en "verdi" prop, vi kan bruke denne prop for å dele en verdi – navn – på tvers av komponenttreet, ved å pakke inn komponenttreet med leverandørkomponenten, gjør vi denne verdien tilgjengelig fra hvor som helst innenfor komponenttreet, uten at det er behov for å overføre denne støtten til hver underordnede komponent individuelt.
I vårt autentiseringseksempel trenger vi en variabel for å holde brukerobjektet, og en slags setter for å manipulere den tilstandsvariabelen, dette er et perfekt brukstilfelle for useState. Når du bruker Context, må du sørge for at du definerer dataene du vil oppgi, og sender disse dataene – brukeren i vårt eksempel – til alle komponenttreet som er nestet inne, så definer en ny tilstandsvariabelbruker inne i leverandørkomponenten og til slutt sender vi både bruker og setUser inne i en matrise som verdien som leverandøren vil eksponere for komponenttreet:
./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>;
};
En annen ting vi kan gjøre er å flytte tilstandsvariabelen «navn» fra hovedappkomponenten til Auth-konteksten og eksponere den for nestede komponenter:
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>;
};
Nå gjenstår det bare å legge appen vår inn i den samme AuthProvider-komponenten vi nettopp eksporterte.
./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>
);
Fordi vi gjengir barnerekvisitten inne i Auth.Provider, kan alle elementene som er nestet inne i AuthProvider-komponenten nå konsumere verdirekvisitten vi sendte til Auth.Provider. Det kan virke forvirrende, men når du først eksperimenterer med det, prøv å gi og konsumere en global – kontekst – tilstand. Tross alt ga det bare mening for meg etter å ha eksperimentert med Context API.
Forbruker Auth-konteksten
Det siste trinnet er enkelt, vi bruker kontekstkroken "useContext" for å få tilgang til verdien som kontekstleverandøren til "Auth" gir, som i vårt tilfelle er matrisen som inneholder bruker og setUser, i følgende kode kan vi bruk useContext for å konsumere Auth-konteksten inne i Navbar. Dette er bare mulig fordi Navbar er nestet inne i app-komponenten, og siden AuthProvider omslutter app-komponenten, kan verdipropp kun konsumeres ved å bruke useContext-kroken. Et annet fantastisk verktøy som React gir ut av boksen for å administrere data som kan nås globalt og også manipuleres av enhver forbrukerkomponent.
./src/components/Navbar.jsx
export default function Navbar() {
const [name, setName] = useContext(Auth);
console.log(name)
return (...)};
Legg merke til hvordan vi ikke aksepterer noen rekvisitter lenger i funksjonskomponenten Navbar(). Vi bruker useContext(Auth) til å konsumere Auth-konteksten i stedet, og henter både navn og setName. Dette betyr også at vi ikke lenger trenger å sende rekvisitten til Navbar:
./src/App.jsx
// ... //
return (
<BrowserRouter>
<div className="h-screen">
<Navbar/> // no need to pass prop anymore
// ... //
Oppdatering av Auth-konteksten
Vi kan også bruke den medfølgende setName-funksjonen for å manipulere tilstandsvariabelen "navn":
./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");
Introduserer useReducer()-kroken
I det forrige eksemplet brukte vi kontekst-APIet til å administrere og dele tilstanden vår nedover komponenttreet, du har kanskje lagt merke til at vi fortsatt bruker useState som basen for logikken vår, og dette er stort sett ok da tilstanden vår fortsatt er et enkelt objekt på dette tidspunktet, men hvis vi skulle utvide mulighetene til autentiseringsflyten vår, vil vi definitivt trenge å lagre mer enn bare e-posten til brukeren som er logget på, og det er her vi går tilbake til begrensningene vi tidligere gikk inn i mht. bruken av useState med kompleks tilstand, heldigvis løser React dette problemet ved å tilby et alternativ til useState for å administrere kompleks tilstand: skriv inn useReducer.
useReducer kan betraktes som en generalisert versjon av useState, den tar to parametere: en reduseringsfunksjon og en starttilstand.
Ingenting interessant å merke seg for den opprinnelige tilstanden, magien skjer inne i reduseringsfunksjonen: den sjekker for typen handling som skjedde, og avhengig av den handlingen, vil reduseringen bestemme hvilke oppdateringer som skal gjelde for tilstanden og returnere dens nye verdi .
Når du ser på koden nedenfor, har reduksjonsfunksjonen to mulige handlingstyper:
-
"LOGG PÅ": i så fall vil brukerstatusen bli oppdatert med den nye brukerlegitimasjonen som er gitt i handlingens nyttelast.
-
"LOGGUT": i så fall vil brukertilstanden fjernes fra den lokale lagringen og settes tilbake til null.
Det er viktig å merke seg at handlingsobjektet inneholder både et typefelt som bestemmer hvilken logikk som skal brukes og et valgfritt nyttelastfelt for å gi data som er nødvendig for å bruke den logikken.
Til slutt returnerer useReducer-kroken den nåværende tilstanden og en ekspedisjonsfunksjon som vi bruker for å sende en handling til reduseringen.
For resten av logikken er den identisk med forrige eksempel:
./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>
);
};
Sende handlinger i stedet for å bruke setState setter-funksjonen – f.eks: setName –
Som vi nettopp nevnte, bruker vi ekspedisjonsfunksjonen til å sende en handling til reduseringen, i den følgende koden starter vi en LOGG IN-handling og vi gir brukerens e-post som en nyttelast, nå vil brukertilstanden bli oppdatert og denne endringen vil utløse en gjengivelse av alle komponenter som er abonnert på brukertilstanden. Det er viktig å påpeke at en re-rendering bare vil utløses hvis en faktisk endring i tilstanden skjer, ingen re-rendering hvis reduseringen returnerer den samme forrige tilstanden.
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 (...)};
Brukerinnlogging
Profftips
Legg merke til hvordan brukerobjektet vi mottar etter en vellykket pålogging nå lagres i localStorage.
Egendefinert krok for pålogging
Nå som vi har et godt grep om useReducer, kan vi videre innkapsle vår påloggings- og utloggingslogikk i sine egne separate tilpassede kroker, ved et enkelt kall til påloggingskroken kan vi håndtere et API-kall til påloggingsruten, hente den nye brukeren legitimasjon og lagre dem i den lokale lagringen, send et LOGIN-anrop for å oppdatere brukertilstanden, alt mens du håndterer feilhåndtering:
./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 };
};
Merk: for mer avanserte React-brukere blant leserne, lurer du kanskje på hvorfor vi ikke brukte den late initialiseringsfunksjonen til useReducer for å hente brukerlegitimasjonen, useReducer godtar en tredje valgfri parameter kalt en init-funksjon, denne funksjonen brukes i tilfelle vi må bruke litt logikk før vi kan få den opprinnelige verdien av staten, grunnen til at vi ikke valgte dette er et enkelt spørsmål om separasjon av bekymringer, koden på denne måten er enklere å forstå og som et resultat enklere å vedlikeholde .
Påloggingsside
Slik ser den øverste delen av påloggingssiden vår ut etter å ha brukt useLogin()-kroken for å trekke ut påloggingsfunksjonen, og påkalle påloggingsfunksjonen med legitimasjon sendt inn av en bruker:
// ... ... //
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 (...)
// ... ... //
Vi deaktiverer også Send-funksjonen når brukeren sender inn skjemaet:
<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>
Og gjengivelse av eventuelle autentiseringsfeil vi mottar fra vår backend:
{
error && <span className="text-red-500 p-2">{error.message}</span>;
}
Vedlikeholde brukertilstanden
Du lurer kanskje på hvorfor vi trenger å lagre brukerobjektet i localStorage, enkelt sagt, vi ønsker å beholde brukeren pålogget så lenge tokenet ikke er utløpt. Å bruke localStorage er en fin måte å lagre deler av JSON på, akkurat som i vårt eksempel. Legg merke til hvordan staten blir slettet hvis du oppdaterer siden etter innlogging. Dette kan løses enkelt ved å bruke en useEffect-hook for å sjekke om vi har et lagret brukerobjekt i localStorage, hvis det er et, logger vi på brukeren automatisk:
// ... ... //
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>
);
};
Egendefinerte kroker for utlogging
Det samme gjelder med utloggingskroken, her sender vi en LOGGUT-handling for å fjerne gjeldende brukerlegitimasjon fra både staten og den lokale lagringen:
./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 };
};
Brukerlogg ut
For å logge ut en bruker, la oss legge til en klikkhendelse til Logg ut-knappen i UserDropdown.jsx, og håndtere den deretter:
./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>
// ... ... //
Beskyttelse av reaksjonsruter
Det siste trinnet i implementeringen av applikasjonen vår er å utnytte den globale brukertilstanden til å kontrollere brukernavigasjon, en rask påminnelse om hvilken oppførsel vi bør oppnå: først blir brukeren møtt av påloggingssiden, fra det punktet kan brukeren bare få tilgang til hjemmesiden etter en vellykket pålogging vil brukeren på samme måte bli omdirigert til påloggingssiden ved en utlogging.
Vi oppnår dette ved hjelp av react-router-dom-biblioteket ved å definere 2 ruter: "/" og "/login", vi kontrollerer hvilken komponent som skal gjengis ved hver rute ved hjelp av den globale autentiseringstilstanden, auth-evaluering til null representerer en ikke-autentisert bruker og omvendt:
./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;
Oppsummeringsdiagram
Avslutning
I denne artikkelen prøvde vi å takle den enkle, men svært vanlige bruken av å implementere tilstandsstyring for en autentiseringsarbeidsflyt, og gå gjennom de forskjellige tilnærmingene, begrunnelsen bak hver enkelt og deres avveininger. Statlig ledelse i rammeverk på klientsiden og i React spesielt er et av de mest omtalte temaene i frontend-fellesskapet, rett og slett fordi det enten kan gjøre eller ødelegge ytelsen og skalerbarheten til applikasjonen din. Selve mengden av de forskjellige teknikkene, mønstrene, bibliotekene og verktøyene som prøver å løse dette problemet med statlig ledelse er overveldende, målet vårt var å gi deg en solid forståelse av praksisen slik at du kan implementere den i din egen applikasjon, for mer avansert teknikker og mønstre for statsadministrasjon i mer komplekse applikasjoner, sjekk ut vår neste artikkel hvor vi kommer inn på redux for skalerbar tilstandsstyring i React.
Kommer snart
ContextAPI vs Redux Toolkit
Redux hadde mange års adopsjon i samfunnet før Redux-verktøysettet, faktisk ble RTK introdusert som en startmal for å starte redux-tilstandsadministrasjon i nye applikasjoner (det opprinnelige navnet var "redux-starter-kit" i oktober 2019), selv om i dag er det en generell konsensus mellom Redux-vedlikeholderne og fellesskapet om at Redux-verktøysettet er den gyldige måten å jobbe med redux på. RTX abstraherer mye av Redux-logikken som gjør den enklere å bruke, med langt mindre detaljert, og oppfordrer utviklere til å følg de beste fremgangsmåtene, en hovedforskjell mellom de to er at Redux ble bygget for å være meningsløs, og gir et minimalt API og forventer at utviklere skal gjøre det meste av det tunge arbeidet ved å skrive sine egne biblioteker for vanlige oppgaver og håndtere kodestruktur, dette resulterte i langsom utviklingstid og rotete kode. Redux-verktøysettet ble lagt til som et ekstra lag med abstraksjon som forhindret utviklere i å falle i de vanlige fallgruvene. Se den offisielle dokumentasjonen for mer innsikt og begrunnelsen fra vedlikeholderne her.