Upravljanje stanja v Reactu: praktični primer

odziv
upravljanje stanja
kontekstni api
Upravljanje stanja v Reactu: praktični primer cover image

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.

User Login

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.

State

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:

Changing user name

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.

  1. 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.

Components Re-rendering

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.

Prop-drilling

./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

JWT Local storage

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>;
}

Rendering Errors

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>
// ... ... //

User Logout

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

Diagram

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.


Career Services background pattern

Karierne storitve

Contact Section background image

Ostanimo v stiku

Code Labs Academy © 2024 Vse pravice pridržane.