React ir ietvars dinamisku klienta puses lietojumprogrammu izveidei daudziem izstrādātājiem. Šo lietojumprogrammu dinamiskais raksturs ir saistīts ar elastību un paplašināto iespēju un funkciju sarakstu, kas ir iespējamas klienta pusē, kas ļāva izstrādātājiem izveidot pilnvērtīgas lietojumprogrammas, kas tiek ielādētas pārlūkprogrammā dažu sekunžu laikā, un tas nebija iespējams. iespējams (vai ļoti apgrūtinoši) statiskā tīmekļa laikos.
Līdz ar šo iespēju paplašināšanos radās stāvokļa pārvaldības jēdziens, jo klienta puses lietojumprogrammu sarežģītība pieaug, nepieciešamība saglabāt vietējo stāvokli kļūst par sašaurinājumu, ja tas netiek pareizi apstrādāts un tiek izstrādāts, ņemot vērā mērogojamību.
Šis jautājums tika risināts daudzos ietvaros, izmantojot dažādas pieejas un koncentrējoties uz dažādām apakšproblēmu kopām, tāpēc ir svarīgi, lai būtu augsta līmeņa izpratne par izvēles ietvara ekosistēmu, lai novērtētu katra lietojumprogrammas vajadzības un izmantotu pareizo pieeju, ievērojot šīs prasības. metriku. Šis raksts sniegs jums īsu pārskatu par izplatītākajām stāvokļa pārvaldības problēmām un mēģinās ieviest dažādas pieejas (useState, Context API), kā atbildi uz to. Lai gan šajā rakstā tiks piedāvāti vairāki risinājumi, tas koncentrēsies tikai uz mazāka mēroga izaicinājumiem, bet turpmākajos rakstos mēs apskatīsim sarežģītākas tēmas.
Autentifikācijas darbplūsma
Kods, kas parādīts visā rakstā, ir atrodams šeit.
Tiešraides priekšskatījuma saitei var piekļūt šeit.
Apsveriet gadījumu, kad mēs ieviešam React lietojumprogrammas autentifikācijas procesu.
Kā parādīts iepriekš redzamajā GIF attēlā, mēs vēlamies ļaut lietotājiem pieteikties vai reģistrēties mūsu lietojumprogrammā, izmantojot akreditācijas datus. Ja ir norādīti derīgi akreditācijas dati, lietotājs tiks pieteikts, lietojumprogramma automātiski pāries uz sākumlapu un lietotājs var turpināt lietot lietojumprogrammu.
Tāpat, ja lietotājs izrakstās, sākumlapas resursi tiks aizsargāti aiz pieteikšanās, pieteikšanās lapa būs vienīgā lapa, kurai lietotājs var piekļūt.
Domājot par šo darbplūsmu saistībā ar ieviešanu, mums būtu galvenais komponents ar nosaukumu App. Lietotnes komponents novirzīs lietotāju uz vienu no divām lapām: Sākums vai Pieteikšanās, pašreizējais lietotāja stāvoklis (pieteicies, atteicies) noteiks, kurš. lapa, uz kuru lietotājs tiek novirzīts, lietotāja pašreizējā stāvokļa maiņai (piemēram, pārejot no pieteikšanās uz atteikšanos) ir jāizraisa tūlītēja novirzīšana uz atbilstošo lapu.
Kā parādīts iepriekš redzamajā attēlā, mēs vēlamies, lai lietotnes komponents ņemtu vērā pašreizējo stāvokli un, pamatojoties uz šo pašreizējo stāvokli, renderētu tikai vienu no divām lapām — Sākums vai Pieteikšanās.
Ja lietotājs ir nulle, tas nozīmē, ka mums nav neviena autentificēta lietotāja, tāpēc mēs automātiski pārejam uz pieteikšanās lapu un aizsargājam sākumlapu, izmantojot nosacījumu renderēšanu. Ja lietotājs pastāv, mēs rīkojamies pretēji.
Tagad, kad mums ir laba izpratne par to, kas būtu jāievieš, izpētīsim dažas iespējas, bet vispirms iestatīsim mūsu React projektu.
Projekta repo satur aizmugursistēmas lietojumprogrammas paraugu, ko izmantosim, lai ieviestu priekšgala pusi (mēs tajā neiedziļināsimies, jo tas šeit nav galvenais, taču kods tika apzināti saglabāts vienkāršs, lai jums ar to nebūtu grūti )
Mēs sākam, izveidojot šādas lapas un komponentus:
-
Mājas lapa
-
Pieteikšanās lapa
-
Rediģēt lietotāja lapu
-
Navigbar komponents
-
UserDropdown komponents
React lietojumprogrammai ar vairākām lapām ir nepieciešama pareiza navigācija, tāpēc mēs varam izmantot react-router-dom, lai izveidotu globālu pārlūkprogrammas maršrutētāja kontekstu un reģistrētu dažādus React maršrutus.
yarn add react-router-dom
Mēs zinām, ka daudzi lasītāji izvēlas sekot līdzi pamācībām, tāpēc šeit ir sākuma veidne, kas palīdzēs jums rīkoties. Šī sākuma filiāle izmanto DaisyUI iepriekš definētiem TailwindCSS JSX komponentiem. Tas ietver visus komponentus, lapas un jau iestatīto maršrutētāju. Ja apsverat turpināt šo pamācību un pats izveidot visu autentifikācijas plūsmu, veicot vienkāršas darbības, vispirms izveidojiet repozitoriju. Kad esat izveidojis repozitoriju, klonējiet to un sāciet no start-here filiāles**:**
git clone git@github.com:<your-username>/fullstack-resourcify.git
Kad esat pavilcis sākuma šeit zaru:
-
Atveriet projektu ar vēlamo koda redaktoru
-
Mainīt direktoriju uz frontend/
-
Instalējiet atkarības: dzija
-
Sāciet izstrādes serveri: yarn dev·
Priekšskatījumam vajadzētu izskatīties apmēram šādi:
Navigācijas joslā (augšējā labajā pusē) atveidotais nosaukums ir galvenajā lietotnes komponentā definēts stāvokļa mainīgais. Tas pats mainīgais tiek nodots gan navigācijas joslai, gan sākumlapai. Iepriekš izmantotā vienkāršā veidlapa faktiski atjaunina “name” stāvokļa mainīgo no EditPage komponenta.
Divās tālāk aprakstītajās pieejās tiks apskatīta ieviešanas informācija.
Pirmā pieeja: useState
useState()
useState ir viens no visbiežāk izmantotajiem React āķiem, kas ļauj izveidot un mainīt stāvokli React Functional komponentā. Komponentam useState ir ļoti vienkārša ieviešana, un to ir viegli lietot: lai izveidotu jaunu stāvokli, jums ir jāizsauc useState ar sava stāvokļa sākotnējo vērtību, un useState āķis atgriezīs masīvu, kurā ir divi mainīgie: pirmais ir stāvoklis. mainīgais, ko varat izmantot, lai atsauktos uz savu stāvokli, un otrs ir funkcija, ko izmantojat, lai mainītu stāvokļa vērtību: diezgan vienkārši.
Kā būtu, ja mēs to redzētu darbībā? Navigācijas joslā (augšējā labajā pusē) atveidotais nosaukums ir galvenajā lietotnes komponentā definēts stāvokļa mainīgais. Tas pats mainīgais tiek nodots gan navigācijas joslai, gan sākumlapai. Iepriekš izmantotā vienkāršā veidlapa faktiski atjaunina “name” stāvokļa mainīgo no EditPage komponenta. Secinājums ir šāds: useState ir galvenais āķis, kas pieņem sākotnējo stāvokli kā parametru un atgriež divus mainīgos, kam ir divas vērtības, stāvokļa mainīgais satur sākotnējo stāvokli un tā paša stāvokļa mainīgā iestatītāja funkciju.
Sadalīsim to un redzēsim, kā tas tika ieviests.
1. Stāvokļa mainīgā “name” izveide:
./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 (...)};
Rekvizīti
Rekvizīti ir viens no react komponenta pamatelementiem. Konceptuāli, ja uzskatāt, ka React funkcionālais komponents ir Javascript funkcija, tad rekvizīti ir tikai funkcijas parametri, kas apvieno rekvizītus un useState hook var piedāvāt stabilu ietvaru. stāvokļa pārvaldībai vienkāršā React lietojumprogrammā.
Reakcijas rekvizīti tiek nodoti kā atribūti pielāgotajiem komponentiem. Atribūtus, kas nodoti kā rekvizīti, var destrukturēt no butaforijas objekta, pieņemot to kā argumentu, līdzīgi kā šis:
Rekvizīti
<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>
Rekvizītus var pieņemt un izmantot funkcionālā komponentā, kas ir līdzīgs parasto funkciju argumentiem. Tas ir tāpēc, ka “nosaukums” tiek nodots kā palīgs Home komponentam, tāpēc mēs varam to atveidot tajā pašā komponentā. Nākamajā piemērā mēs akceptējam nodoto rekvizītu, izmantojot destrukturēšanas sintaksi, lai no rekvizītu objekta izvilktu nosaukuma rekvizītu.
Rekvizītu pieņemšana
./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>
... ...
Pro padoms
Atveriet pārlūkprogrammas konsoli un ievērojiet, kā visi komponenti, kas izmanto rekvizītu “nosaukums”, tiek atkārtoti renderēti, mainoties stāvoklim. Veicot manipulācijas ar stāvokļa mainīgo, React saglabās nākamo stāvokli, atkal atveidos jūsu komponentu ar jaunajām vērtībām un atjauninās lietotāja saskarni.
useState Trūkumi
Rekvizīti-urbšana
Rekvizītu urbšana ir termins, kas apzīmē komponentu hierarhiju, kurā komponentu kopai ir nepieciešami noteikti elementi, ko nodrošina vecākkomponents, un parasti nepieredzējuši izstrādātāji izmanto šo rekvizītu pārvietošanu pa visu komponentu ķēdi. pieeja ir tāda, ka izmaiņas jebkurā no šiem rekvizītiem izraisīs visu komponentu ķēdes atkārtotu atveidošanu, efektīvi palēninot visas lietojumprogrammas darbību šo nevajadzīgo renderēšanas rezultātā, ķēdes vidū esošos komponentus, kuriem nav nepieciešami šie rekvizīti. darbojas kā nesējs rekvizītu pārvietošanai.
Piemērs:
- Stāvokļa mainīgais tika definēts galvenajā lietotnes komponentā, izmantojot āķi useState().
-
Navbar komponentam tika nodots nosaukums
-
Tas pats rekvizīts ir pieņemts navigācijas joslā un vēlreiz nodots UserDropdown komponentam
-
UserDropdown ir pēdējais pakārtotais elements, kas pieņem rekvizītu.
./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>
</>
);
}
Iedomājieties, cik grūti ir uzturēt React lietotni ar dažādiem komponentu slāņiem, kas pāriet un atveido vienādu stāvokli.
Pieaug sarežģītība un koda kvalitāte
Izmantojot useState un rekvizītus kā vienīgos stāvokļa pārvaldības līdzekļus react lietojumprogrammā, kodu bāze var ātri kļūt sarežģītāka, jo tai ir jāpārvalda desmitiem vai simtiem stāvokļa mainīgo, kas var būt viens otra dublikāti, izkaisīti dažādos failos un komponenti var būt diezgan biedējoši, jebkuras izmaiņas noteiktā stāvokļa mainīgajā prasīs rūpīgi apsvērt atkarības starp komponentiem, lai izvairītos no iespējamās papildu atveidošanas jau tā lēnā lietojumprogrammā.
Otrā pieeja: konteksta API
Context API ir React mēģinājums atrisināt trūkumus, kas rodas, izmantojot tikai rekvizītus un useState stāvokļa pārvaldībai, jo īpaši konteksta API ir atbilde uz iepriekš minēto problēmu saistībā ar nepieciešamību nodot rekvizītus visā komponentu kokā. Izmantojot kontekstu, varat definēt stāvokli datiem, ko uzskatāt par globāliem, un piekļūt to stāvoklim no jebkura punkta komponentu kokā: vairs nav jāveic urbšana.
Ir svarīgi norādīt, ka konteksta API sākotnēji tika izstrādāta, lai atrisinātu globālo datu koplietošanas problēmu, tādu datu kā lietotāja interfeisa motīvi, autentifikācijas informācija, kas ir mūsu lietošanas gadījums, valodas un tamlīdzīgi), cita veida datiem, kas ir jākoplieto. starp vairākiem komponentiem, taču tas ne vienmēr ir globāls visai lietojumprogrammai, konteksts var nebūt labākais risinājums. Atkarībā no lietošanas gadījuma varat apsvērt citas metodes, piemēram, komponentu sastāvu, kas neietilpst šī raksta darbības jomā.
Pārslēgšanās uz kontekstu
Auth konteksta izveide
CreateContext ir vienkāršs, tas rada jaunu konteksta mainīgo, tajā tiek izmantots viens neobligāts parametrs: konteksta mainīgā noklusējuma vērtība.
Apskatīsim to darbībā, vispirms izveidojiet jaunu mapi “konteksti” un tajā jaunu failu “Auth.jsx”. Lai izveidotu jaunu kontekstu, mums ir jāizsauc funkcija createContext(), atgrieztā vērtība jāpiešķir jaunam mainīgajam Auth, kas tiks eksportēts pēc tam:
./src/contexts/Auth.jsx
import { createContext } from "react";
export const Auth = createContext();
Norādiet autentifikācijas kontekstu
Tagad mums ir jāatklāj konteksta mainīgais "Auth", ko mēs izveidojām iepriekš, lai to panāktu, mēs izmantojam konteksta nodrošinātāju, konteksta nodrošinātājs ir komponents, kuram ir "vērtības" rekvizīts, mēs varam izmantot šo parametru, lai kopīgotu vērtību — nosaukums. – visā komponentu kokā, iesaiņojot komponentu koku ar nodrošinātāja komponentu, mēs padarām šo vērtību pieejamu no jebkuras vietas komponentu koka iekšienē, bez nepieciešamības nodot šo atbalstu katram pakārtotajam komponentam atsevišķi.
Mūsu autentifikācijas piemērā mums ir nepieciešams mainīgais, lai turētu lietotāja objektu, un sava veida iestatītājs, lai manipulētu ar šo stāvokļa mainīgo. Šis ir ideāls useState lietošanas gadījums. Izmantojot kontekstu, jums ir jāpārliecinās, ka definējat datus, ko vēlaties sniegt, un nododat šos datus (lietotājs mūsu piemērā) visam komponentu kokam, kas ir ligzdots iekšpusē, tāpēc nodrošinātāja komponentā definējiet jaunu stāvokļa mainīgo lietotājs un visbeidzot, mēs nododam gan user, gan setUser masīvā kā vērtību, ko pakalpojumu sniedzējs parādīs komponentu kokam:
./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>;
};
Vēl viena lieta, ko mēs varam darīt, ir pārvietot mūsu “nosaukuma” stāvokļa mainīgo no galvenā lietotnes komponenta uz autentifikācijas kontekstu un pakļaut to ligzdotiem komponentiem:
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>;
};
Tagad atliek tikai ievietot mūsu lietotni tajā pašā AuthProvider komponentā, kuru tikko eksportējām.
./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>
);
Tā kā mēs atveidojam atvasinātos rekvizītus iekšā Auth.Provider, visi elementi, kas ir ligzdoti AuthProvider komponentā, tagad var patērēt vērtību rekvizītu, ko nodevām Auth.Provider. Tas varētu šķist mulsinoši, taču, kad ar to eksperimentējat, mēģiniet nodrošināt un izmantot globālu — konteksta — stāvokli. Galu galā tas man bija jēga tikai pēc eksperimentēšanas ar konteksta API.
Auth konteksta izmantošana
Pēdējais solis ir vienkāršs. Mēs izmantojam konteksta āķi "useContext", lai piekļūtu vērtībai, ko nodrošina "Auth" konteksta nodrošinātājs, kas mūsu gadījumā ir masīvs, kurā ir lietotājs un setUser. Nākamajā kodā mēs varam izmantojiet useContext, lai izmantotu autentifikācijas kontekstu navigācijas joslā. Tas ir iespējams tikai tāpēc, ka Navigbar ir ligzdots lietojumprogrammas komponentā un tā kā AuthProvider apņem lietotnes komponentu, vērtību var izmantot, tikai izmantojot useContext āķi. Vēl viens satriecošs rīks React nodrošina iespēju pārvaldīt visus datus, kuriem var piekļūt globāli un kurus var arī manipulēt ar jebkuru patērētāju komponentu.
./src/components/Navbar.jsx
export default function Navbar() {
const [name, setName] = useContext(Auth);
console.log(name)
return (...)};
Ievērojiet, kā mēs vairs nepieņemam nekādus rekvizītus Navbar() funkcionālajā komponentā. Mēs izmantojam useContext(Auth), lai izmantotu Auth kontekstu, izmantojot gan nosaukumu, gan setName. Tas arī nozīmē, ka mums vairs nav jānodod rekvizīti Navbar:
./src/App.jsx
// ... //
return (
<BrowserRouter>
<div className="h-screen">
<Navbar/> // no need to pass prop anymore
// ... //
Autentifikācijas konteksta atjaunināšana
Mēs varam arī izmantot sniegto funkciju setName, lai manipulētu ar stāvokļa mainīgo “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");
Iepazīstinām ar useReducer() āķi
Iepriekšējā piemērā mēs izmantojām konteksta API, lai pārvaldītu un koplietotu mūsu stāvokli komponentu kokā, iespējams, pamanījāt, ka mēs joprojām izmantojam useState kā savas loģikas pamatu, un tas lielākoties ir pareizi, jo mūsu stāvoklis joprojām ir vienkāršs objekts. šajā brīdī, bet, ja mēs paplašinātu savas autentifikācijas plūsmas iespējas, mums noteikti būs jāsaglabā vairāk nekā tikai tā lietotāja e-pasts, kurš pašlaik ir pieteicies, un šeit mēs atgriežamies pie ierobežojumiem, kas iepriekš bija saistīti ar par laimi, useState izmantošana ar sarežģītu stāvokli, par laimi, React atrisina šo problēmu, nodrošinot alternatīvu useState kompleksa stāvokļa pārvaldīšanai: ievadiet useReducer.
useReducer var uzskatīt par vispārinātu useState versiju, tam nepieciešami divi parametri: reduktora funkcija un sākotnējais stāvoklis.
Sākotnējam stāvoklim nav nekā interesanta, jo maģija notiek reduktora funkcijā: tā pārbauda notikušās darbības veidu, un atkarībā no šīs darbības reduktors noteiks, kādus atjauninājumus piemērot stāvoklim, un atgriezīs tā jauno vērtību. .
Aplūkojot tālāk norādīto kodu, reduktora funkcijai ir divi iespējamie darbību veidi:
-
"LOGIN": šādā gadījumā lietotāja statuss tiks atjaunināts ar jaunajiem lietotāja akreditācijas datiem, kas tiek nodrošināti darbības slodzes ietvaros.
-
"LOGOUT": šādā gadījumā lietotāja statuss tiks noņemts no vietējās krātuves un iestatīts atpakaļ uz nulli.
Ir svarīgi ņemt vērā, ka darbības objektā ir gan tipa lauks, kas nosaka, kāda loģika jāpiemēro, gan neobligāts lietderīgās slodzes lauks, lai sniegtu datus, kas nepieciešami šīs loģikas lietošanai.
Visbeidzot, āķis useReducer atgriež pašreizējo stāvokli un nosūtīšanas funkciju, ko mēs izmantojam, lai nodotu darbību reduktoram.
Attiecībā uz pārējo loģiku tas ir identisks iepriekšējam piemēram:
./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>
);
};
Nosūtīšanas darbības, nevis izmantot setState iestatītāja funkciju – piemēram: setName –
Kā mēs tikko minējām, mēs izmantojam nosūtīšanas funkciju, lai nodotu darbību reducētājam, nākamajā kodā mēs uzsākam LOGIN darbību un sniedzam lietotāja e-pastu kā lietderīgo slodzi, tagad lietotāja stāvoklis tiks atjaunināts un šīs izmaiņas tiks aktivizētas. visu lietotāja statusā abonēto komponentu atkārtota atveidošana. Ir svarīgi norādīt, ka atkārtota renderēšana tiks aktivizēta tikai tad, ja notiek faktiskas stāvokļa izmaiņas. Atkārtota renderēšana netiks veikta, ja reduktors atgriež to pašu iepriekšējo stāvokli.
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 (...)};
Lietotāja pieteikšanās
Pro padoms
Ievērojiet, kā lietotāja objekts, ko saņemam pēc veiksmīgas pieteikšanās, tagad tiek saglabāts lokālajā krātuvē.
Pielāgots pieteikšanās āķis
Tagad, kad esam labi pārvaldījuši useReducer, mēs varam vēl vairāk iekapsulēt savu pieteikšanās un atteikšanās loģiku to atsevišķos pielāgotajos āķos, ar vienu pieteikšanās āķa izsaukumu, mēs varam apstrādāt API izsaukumu uz pieteikšanās maršrutu, izgūt jauno lietotāju. akreditācijas datus un saglabājiet tos vietējā krātuvē, nosūtiet LOGIN zvanu, lai atjauninātu lietotāja stāvokli, vienlaikus veicot kļūdu apstrādi:
./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 };
};
Piezīme: pieredzējušākiem React lietotājiem lasītāju vidū varētu rasties jautājums, kāpēc mēs neizmantojām useReducer slinko inicializācijas līdzekli, lai izgūtu lietotāja akreditācijas datus, useReducer pieņem trešo izvēles parametru, ko sauc par init funkciju. Šī funkcija tiek izmantota gadījumā mums ir jāpiemēro kāda loģika, pirms mēs varam iegūt stāvokļa sākotnējo vērtību, iemesls, kāpēc mēs to neizvēlējāmies, ir vienkāršs problēmu nodalīšanas jautājums, kods šādā veidā ir vienkāršāk saprotams un rezultātā vienkāršāk uzturējams. .
Pieteikšanās lapa
Lūk, kā izskatās mūsu Pieteikšanās lapas augšdaļa pēc tam, kad tiek izmantots āķis useLogin(), lai izvilktu pieteikšanās funkciju, un izsaukta pieteikšanās funkcija ar lietotāja iesniegtiem akreditācijas datiem:
// ... ... //
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 (...)
// ... ... //
Mēs arī atspējojam funkciju Iesniegt, kad lietotājs iesniedz veidlapu:
<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>
Un visu autentifikācijas kļūdu renderēšana, ko saņemam no mūsu aizmugursistēmas:
{
error && <span className="text-red-500 p-2">{error.message}</span>;
}
Lietotāja stāvokļa uzturēšana
Jums varētu rasties jautājums, kāpēc mums ir jāsaglabā lietotāja objekts lokālajā krātuvē, vienkārši sakot, mēs vēlamies, lai lietotājs būtu pierakstījies, kamēr marķiera derīguma termiņš nav beidzies. LocalStorage izmantošana ir lielisks veids, kā saglabāt JSON bitus, tāpat kā mūsu piemērā. Ievērojiet, kā statuss tiek izdzēsts, ja pēc pieteikšanās atsvaidzinat lapu. To var viegli atrisināt, izmantojot useEffect āķi, lai pārbaudītu, vai vietējā krātuvē ir saglabāts lietotāja objekts; ja tāds ir, mēs automātiski piesakāmies lietotājam:
// ... ... //
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>
);
};
Pielāgoti āķi izteikšanai
Tas pats attiecas uz Atteikšanās āķi, šeit mēs nosūtām LOGOUT darbību, lai noņemtu pašreizējos lietotāja akreditācijas datus gan no štata, gan vietējās krātuves:
./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 };
};
Lietotāja atteikšanās
Lai atteiktos no lietotāja, pievienosim klikšķa notikumu pogai Atteikšanās, kas atrodas UserDropdown.jsx, un attiecīgi rīkojamies:
./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>
// ... ... //
Reaģēšanas maršrutu aizsardzība
Pēdējais solis mūsu lietojumprogrammas ieviešanā ir globālā lietotāja stāvokļa izmantošana, lai kontrolētu lietotāja navigāciju. Tas ir ātrs atgādinājums par to, kāda rīcība mums ir jāpanāk: sākotnēji lietotāju sveicina pieteikšanās lapa, no šī brīža lietotājs var piekļūt tikai sākumlapai. pēc veiksmīgas pieteikšanās, tāpat lietotājs pēc atteikšanās tiks novirzīts uz pieteikšanās lapu.
Mēs to panākam ar react-router-dom bibliotēkas palīdzību, definējot 2 maršrutus: "/" un "/login", mēs kontrolējam, kuru komponentu renderēt katrā maršrutā, izmantojot globālo autentifikācijas stāvokli., autentifikācijas novērtējums līdz nullei apzīmē neautentificētu lietotāju un otrādi:
./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;
Kopsavilkuma diagramma
Noslēgums
Šajā rakstā mēs mēģinājām risināt vienkāršo, bet ļoti izplatīto gadījumu, kad autentifikācijas darbplūsmā tiek ieviesta stāvokļa pārvaldība, izpētot dažādas pieejas, katras no tām loģiskos iemeslus un to kompromisus. Stāvokļa pārvaldība klienta puses ietvaros un jo īpaši React ir viena no visvairāk apspriestajām tēmām priekšgala kopienā, jo tā var vai nu uzlabot vai sabojāt jūsu lietojumprogrammas veiktspēju un mērogojamību. Milzīgs dažādu paņēmienu, modeļu, bibliotēku un rīku daudzums, kas mēģina atrisināt šo valsts pārvaldības problēmu, ir milzīgs, mūsu mērķis bija sniegt jums skaidru izpratni par praksi, lai jūs varētu to ieviest savā lietojumprogrammā, lai iegūtu progresīvākus. stāvokļu pārvaldības paņēmienus un modeļus sarežģītākās lietojumprogrammās, skatiet mūsu nākamo rakstu, kurā mēs iepazīstamies ar redux mērogojamai stāvokļa pārvaldībai programmā React.
Drīzumā
ContextAPI vs Redux Toolkit
Redux daudzus gadus tika pieņemts kopienā pirms Redux rīkkopas, patiesībā RTK tika ieviesta kā sākuma veidne redux stāvokļa pārvaldības sāknēšanai jaunās lietojumprogrammās (tā sākotnējais nosaukums bija “redux-starter-kit” 2019. gada oktobrī), lai gan šodien starp Redux uzturētājiem un kopienu ir vispārēja vienprātība, ka Redux rīkkopa ir derīgs veids, kā strādāt ar redux.RTX abstrahē daudz Redux loģikas, kas padara to vieglāk lietojamu ar daudz mazāk izteiktu saturu un mudina izstrādātājus ievērojiet labāko praksi, viena no galvenajām atšķirībām starp abām ir tā, ka Redux tika izveidots tā, lai nebūtu viedokļu, nodrošinot minimālu API un sagaidot, ka izstrādātāji veiks lielāko daļu smagā darba, rakstot paši savas bibliotēkas parastajiem uzdevumiem un risinot koda struktūru. tas izraisīja lēnu izstrādes laiku un nekārtīgu kodu, Redux rīkkopa tika pievienota kā papildu abstrakcijas slānis, kas neļauj izstrādātājiem iekļūt tā izplatītajās kļūmēs. Plašāku ieskatu un uzturētāju sniegto pamatojumu skatiet oficiālajā dokumentācijā šeit.