Պետական ​​կառավարում React-ում. Գործնական օրինակ

արձագանքել
պետության կառավարում
համատեքստային api
Պետական ​​կառավարում React-ում. Գործնական օրինակ cover image

React-ը շատ ծրագրավորողների համար հաճախորդի կողմից դինամիկ հավելվածներ ստեղծելու հիմնական շրջանակն է: Այս հավելվածների դինամիկ բնույթը գալիս է հաճախորդի կողմից հնարավոր հնարավորությունների և առանձնահատկությունների ընդլայնված ցանկից և հնարավորություններից, որոնք ծրագրավորողներին հնարավորություն են տվել հաշված վայրկյանների ընթացքում բրաուզերի վրա բեռնվող լիարժեք հավելվածներ ստեղծել, ինչը չի եղել: հնարավոր է (կամ շատ ծանր) ստատիկ ցանցի օրերին:

Հնարավորությունների այս ընդլայնմամբ առաջացավ պետության կառավարման հայեցակարգը, քանի որ հաճախորդի կողմից հավելվածների բարդությունն աճում է, տեղական պետությունը պահելու անհրաժեշտությունը դառնում է ինքնին խոչընդոտ, եթե ճիշտ չմշակվի և մտահղացվի մասշտաբայնությունը:

Այս խնդրին անդրադարձել են բազմաթիվ շրջանակներ՝ հետևելով տարբեր մոտեցումներին և կենտրոնանալով տարբեր ենթախնդիրների վրա, այդ իսկ պատճառով կարևոր է ունենալ ընտրության շրջանակի էկոհամակարգի բարձր մակարդակի պատկերացում՝ յուրաքանչյուր հավելվածի կարիքները գնահատելու և դրանց հետևելով ճիշտ մոտեցում կիրառելու համար։ չափումներ. Այս հոդվածը ձեզ կտրամադրի ընդհանուր պետական ​​կառավարման հիմնախնդիրների համառոտ ակնարկ և կփորձի ներկայացնել տարբեր մոտեցումներ (useState, Context API) որպես դրա պատասխան: Թեև այս հոդվածը կներկայացնի բազմաթիվ լուծումներ, այն կկենտրոնանա միայն ավելի փոքր մասշտաբով մարտահրավերների վրա, մենք առաջիկա հոդվածներում կանդրադառնանք ավելի առաջադեմ թեմաներին:

Նույնականացման աշխատանքային հոսք

Ամբողջ հոդվածում ցուցադրված կոդը կարելի է գտնել այստեղ:

Կենդանի նախադիտման հղումը կարող եք մուտք գործել այստեղ:

Դիտարկենք այն դեպքը, երբ մենք իրականացնում ենք նույնականացման գործընթացը React հավելվածի համար:

User Login

Ինչպես ցույց է տրված վերը նշված GIF-ում, մենք ցանկանում ենք թույլ տալ օգտվողներին մուտք գործել կամ գրանցվել մեր հավելվածում՝ օգտագործելով հավատարմագրերը: Եթե ​​տրամադրվեն վավեր հավատարմագրեր, օգտատերը մուտք կգործի, հավելվածն ավտոմատ կերպով կանցնի գլխավոր էջ, և օգտատերը կարող է շարունակել օգտագործել հավելվածը:

Նմանապես, եթե օգտվողը դուրս գա, գլխավոր էջի ռեսուրսները պաշտպանված կլինեն մուտքի հետևում, մուտքի էջը կլինի միակ էջը, որը հասանելի է օգտատիրոջը:

Մտածելով այս աշխատանքի ընթացքի մասին՝ մենք կունենանք App անունով հիմնական բաղադրիչ, App բաղադրիչը կվերահղորդի օգտվողին երկու էջերից մեկը՝ Տուն կամ Մուտք, օգտատիրոջ ներկայիս վիճակը (մուտք, դուրս եկած) կթելադրի էջ, ուր օգտատերը վերահղված է, օգտատիրոջ ներկայիս վիճակի փոփոխությունը (օրինակ՝ մուտքից դուրս եկածի փոփոխությունը) պետք է ակնթարթային վերահղում առաջացնի դեպի համապատասխան էջ:

State

Ինչպես ցույց է տրված վերևի նկարում, մենք ցանկանում ենք, որ Հավելվածի բաղադրիչը հաշվի առնի ներկայիս վիճակը և միայն ցուցադրի երկու էջերից մեկը՝ Տուն կամ Մուտք, հիմնվելով այդ ընթացիկ վիճակի վրա:

Եթե ​​օգտատերը զրոյական է, դա նշանակում է, որ մենք չունենք վավերացված օգտատեր, ուստի մենք ավտոմատ կերպով նավարկում ենք մուտքի էջ և պաշտպանում ենք գլխավոր էջը՝ պայմանական մատուցման միջոցով: Եթե ​​օգտատերը կա, մենք հակառակն ենք անում։

Այժմ, երբ մենք լավ հասկանում ենք, թե ինչ պետք է իրականացվի, եկեք ուսումնասիրենք մի քանի տարբերակներ, բայց նախ եկեք ստեղծենք մեր React նախագիծը,

Ծրագրի ռեպո-ն պարունակում է օրինակելի հետին պլանի հավելված, որը մենք կօգտագործենք ճակատային կողմն իրականացնելու համար (մենք դրան չենք անդրադառնա, քանի որ այստեղ հիմնական շեշտադրումը չէ, բայց կոդը միտումնավոր պարզ է պահվել, այնպես որ դուք դրա հետ դժվարություն չեք ունենա: )

Մենք սկսում ենք ստեղծելով հետևյալ էջերը և բաղադրիչները.

  • Գլխավոր էջ

  • Մուտքի էջ

  • Խմբագրել օգտվողի էջը

  • Navbar բաղադրիչ

  • UserDropdown բաղադրիչ

Բազմաթիվ էջերով React հավելվածին անհրաժեշտ է պատշաճ նավարկություն, դրա համար մենք կարող ենք օգտագործել react-router-dom՝ ստեղծելու բրաուզերի երթուղիչի գլոբալ համատեքստ և գրանցելու տարբեր React երթուղիներ:


yarn add react-router-dom

Մենք գիտենք, որ շատ ընթերցողներ նախընտրում են հետևել ձեռնարկներին, այնպես որ ահա մեկնարկային ձևանմուշը ձեզ արագացնելու համար: Այս մեկնարկային մասնաճյուղն օգտագործում է DaisyUI նախապես սահմանված TailwindCSS JSX բաղադրիչների համար: Այն ներառում է բոլոր բաղադրիչները, էջերը և արդեն տեղադրված երթուղիչը: Եթե ​​դուք մտածում եք հետևել այս ձեռնարկին զուգահեռ՝ կառուցելով ամբողջ վավերականության հոսքը ինքներդ՝ հետևելով պարզ քայլերին, սկզբում սկսեք պատառաքաղել պահեստը: Պահեստը պատառաքաղելուց հետո կլոնավորեք այն և սկսեք start-hereմասնաճյուղից:


git clone git@github.com:<your-username>/fullstack-resourcify.git

Երբ դուք քաշեք start-here մասնաճյուղը.

  • Բացեք նախագիծը ձեր նախընտրած կոդերի խմբագրիչով

  • Փոխել գրացուցակը frontend/

  • Տեղադրեք կախվածությունը՝ մանվածք

  • Սկսեք զարգացման սերվեր՝ yarn dev·

Նախադիտումը պետք է նման լինի հետևյալին.

Changing user name

Navbar-ում ներկայացված անունը՝ վերևի աջ կողմում, վիճակի փոփոխական է, որը սահմանված է հավելվածի հիմնական բաղադրիչում: Նույն փոփոխականը փոխանցվում է և՛ Navbar, և՛ Գլխավոր էջ: Վերևում օգտագործված պարզ ձևը իրականում թարմացնում է «անուն» վիճակի փոփոխականը EditPage բաղադրիչից:

Ստորև ներկայացված երկու մոտեցումները կանդրադառնան իրականացման մանրամասներին.

Առաջին մոտեցում. useState

useState()

useState-ը ամենահաճախ օգտագործվող React կեռիկներից մեկն է, որը թույլ է տալիս ստեղծել և մուտացիայի ենթարկել վիճակ React Functional բաղադրիչում: UseState բաղադրիչն ունի իսկապես պարզ իրականացում և հեշտ է օգտագործել. նոր վիճակ ստեղծելու համար անհրաժեշտ է զանգահարել useState ձեր վիճակի սկզբնական արժեքով, և useState կեռիկը կվերադարձնի երկու փոփոխական պարունակող զանգված. առաջինը վիճակը է: փոփոխական, որը կարող եք օգտագործել ձեր վիճակին հղում կատարելու համար, իսկ երկրորդը` ֆունկցիա, որն օգտագործում եք վիճակի արժեքը փոխելու համար. բավականին պարզ:

Ի՞նչ կասեք, որ մենք դա տեսնում ենք գործողության մեջ: Navbar-ում ներկայացված անունը՝ վերևի աջ կողմում, վիճակի փոփոխական է, որը սահմանված է հավելվածի հիմնական բաղադրիչում: Նույն փոփոխականը փոխանցվում է և՛ Navbar, և՛ Գլխավոր էջ: Վերևում օգտագործված պարզ ձևը իրականում թարմացնում է «անուն» վիճակի փոփոխականը EditPage բաղադրիչից: Ստորին գիծը սա է. useState-ը հիմնական կեռիկ է, որն ընդունում է նախնական վիճակը որպես պարամետր և վերադարձնում է երկու արժեք ունեցող երկու փոփոխական՝ նախնական վիճակը պարունակող վիճակի փոփոխականը և նույն վիճակի փոփոխականի համար սահմանող ֆունկցիա:

Եկեք բաժանենք այն և տեսնենք, թե ինչպես է դա իրականացվել ի սկզբանե:

  1. «անուն» վիճակի փոփոխականի ստեղծում.

./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: &#36;{name}`);

 return (...)};

Պահեստամասեր

Props-ը react բաղադրիչի հիմնական կառուցողական բլոկներից մեկն է, կոնցեպտուալ առումով, եթե դուք մտածում եք React ֆունկցիոնալ բաղադրիչի մասին որպես Javascript ֆունկցիա, ապա ռեկորդները ոչ ավելին են, քան ֆունկցիայի պարամետրերը. պարզ React հավելվածում վիճակը կառավարելու համար:

React props-ը փոխանցվում է որպես մաքսային բաղադրիչների ատրիբուտներ: Որպես props փոխանցված ատրիբուտները կարող են ապակառուցվել props օբյեկտից, երբ այն որպես արգումենտ ընդունելիս, նման է սա.

Անցումային հենարաններ

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

Props-ը կարող է ընդունվել և օգտագործվել նորմալ ֆունկցիայի արգումենտներին նման ֆունկցիոնալ բաղադրիչի ներսում: Դա այն պատճառով է, որ «անունը» փոխանցվում է որպես հիմնական բաղադրիչ Home բաղադրիչին, որ մենք կարող ենք այն մատուցել նույն բաղադրիչում: Հետևյալ օրինակում մենք ընդունում ենք անցած հենարանը՝ օգտագործելով ապակառուցողական շարահյուսությունը՝ անվանման հատկությունը props օբյեկտից հանելու համար:

Հավաքածուների ընդունում

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

Բացեք բրաուզերի վահանակը և նկատեք, թե ինչպես են բոլոր բաղադրիչները, որոնք օգտագործում են «անունը», վերարտադրվում են, երբ վիճակը փոխվում է: Պետական ​​փոփոխականը շահարկելիս React-ը կպահի հաջորդ վիճակը, ձեր բաղադրիչը նորից կարտացոլի նոր արժեքներով և կթարմացնի միջերեսը:

Components Re-rendering

օգտագործում Պետական ​​թերությունները

Հենարան-Հորատում

Հենարանների հորատումը տերմին է, որը վերաբերում է բաղադրիչների հիերարխիային, որտեղ բաղադրիչների մի շարք կարիք ունեն որոշակի հենարանների, որոնք տրամադրվում են մայր բաղադրիչի կողմից, ընդհանուր լուծումը, որը սովորաբար օգտագործում է անփորձ ծրագրավորողն այն է, որ այս հենարանները փոխանցեն բաղադրիչների ամբողջ շղթայում, խնդիրը սրա հետ կապված է. մոտեցումն այն է, որ այս հենակետերից որևէ մեկի փոփոխությունը կհանգեցնի բաղադրիչների ամբողջ շղթայի վերաարտադրությանը՝ արդյունավետորեն դանդաղեցնելով ամբողջ կիրառումը այս անհարկի արտապատկերումների արդյունքում՝ շղթայի մեջտեղում գտնվող բաղադրիչները, որոնք չեն պահանջում այդ հենարանները: հանդես գալ որպես հենարաններ փոխանցելու միջոց:

Օրինակ՝

  • Ծրագրի հիմնական բաղադրիչում սահմանվել է վիճակի փոփոխական՝ օգտագործելով useState() կեռը

  • Անվանման հենարան փոխանցվել է Navbar բաղադրիչին

  • Նույն հենարանն ընդունված է Navbar-ում և ևս մեկ անգամ փոխանցվում է UserDropdown բաղադրիչին

  • UserDropdown-ը վերջին երեխա տարրն է, որն ընդունում է հենարանը:

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

Պատկերացրեք, թե որքան դժվար է պահպանել React հավելվածը՝ բաղադրիչների տարբեր շերտերով, որոնք բոլորն անցնում են և ներկայացնում նույն վիճակը:

Աճող բարդություն և կոդի որակ

UseState-ի և props-ի կիրառմամբ՝ որպես պետության կառավարման միակ միջոց react հավելվածում, կոդերի բազան կարող է արագորեն բարդանալ՝ ստիպված լինելով կառավարել տասնյակ կամ հարյուրավոր վիճակի փոփոխականներ, որոնք կարող են լինել միմյանց կրկնօրինակներ, ցրված տարբեր ֆայլերում և բաղադրիչները կարող են բավականին վախեցնող լինել, տվյալ վիճակի փոփոխականի ցանկացած փոփոխություն կպահանջի բաղադրիչների միջև կախվածության մանրակրկիտ դիտարկում՝ առանց այն էլ դանդաղ կիրառման մեջ որևէ հնարավոր լրացուցիչ վերաարտադրումից խուսափելու համար:

Երկրորդ մոտեցում՝ համատեքստային API

Համատեքստային API-ն React-ի փորձն է՝ լուծելու միայն պրոցեսորների և UseState-ի օգտագործման թերությունները պետական ​​կառավարման համար, մասնավորապես, համատեքստային API-ն գալիս է որպես պատասխան այն խնդրին, որը նախկինում նշված էր՝ կապված ամբողջ բաղադրիչ ծառի վրա հենակետեր փոխանցելու անհրաժեշտության հետ: Համատեքստի օգտագործմամբ դուք կարող եք սահմանել վիճակ այն տվյալների համար, որոնք դուք համարում եք գլոբալ և մուտք գործեք դրա վիճակը բաղադրիչի ծառի ցանկացած կետից. այլևս ոչ մի հենարանային հորատում:

Կարևոր է նշել, որ համատեքստի API-ն ի սկզբանե ստեղծվել է գլոբալ տվյալների փոխանակման խնդիրը լուծելու համար, այնպիսի տվյալներ, ինչպիսիք են UI թեմաները, նույնականացման տեղեկատվությունը, որը մեր օգտագործման դեպքն է, լեզուներ և այլն), այլ տեսակի տվյալների համար, որոնք պետք է համօգտագործվեն: մեկից ավելի բաղադրիչների մեջ, բայց պարտադիր չէ, որ գլոբալ լինի բոլոր հավելվածների համար, համատեքստը կարող է լավագույն տարբերակը չլինել, կախված օգտագործման դեպքից, դուք կարող եք դիտարկել այլ տեխնիկա, ինչպիսիք են բաղադրիչի կազմը: /composition-vs-inheritance.html), որը դուրս է այս հոդվածի շրջանակներից:

Անցում համատեքստին

Auth համատեքստի ստեղծում

createContext-ը պարզ է, այն ստեղծում է համատեքստի նոր փոփոխական, այն ընդունում է մեկ կամընտիր պարամետր՝ համատեքստի փոփոխականի լռելյայն արժեքը:

Եկեք տեսնենք սա գործողության մեջ, նախ ստեղծեք նոր թղթապանակ «համատեքստեր» և նոր ֆայլ դրա ներսում «Auth.jsx»: Նոր համատեքստ ստեղծելու համար մենք պետք է կանչենք createContext() ֆունկցիան, վերադարձված արժեքը վերագրենք նոր Auth փոփոխականին, որը կարտահանվի հաջորդիվ.

./src/contexts/Auth.jsx

import { createContext } from "react";

export const Auth = createContext();

Տրամադրել Auth համատեքստը

Այժմ մենք պետք է բացահայտենք «Auth» համատեքստի փոփոխականը, որը մենք ստեղծել ենք նախկինում, դրան հասնելու համար մենք օգտագործում ենք համատեքստի մատակարարը, համատեքստի մատակարարը բաղադրիչ է, որն ունի «արժեք» հենարան, մենք կարող ենք օգտագործել այս հենարանը՝ արժեք – անուն կիսելու համար: – բաղադրիչ ծառի վրայով, բաղադրիչ ծառը մատակարարող բաղադրիչով փաթաթելով, մենք այդ արժեքը հասանելի ենք դարձնում բաղադրիչի ծառի ցանկացած կետից՝ առանց այդ հենարանը յուրաքանչյուր երեխայի բաղադրիչին առանձին փոխանցելու անհրաժեշտության:

Մեր իսկորոշման օրինակում մեզ անհրաժեշտ է փոփոխական՝ օգտագործողի օբյեկտը պահելու համար, և ինչ-որ կարգավորիչ՝ այդ վիճակի փոփոխականը շահարկելու համար, սա օգտագործման կատարյալ դեպք է useState-ի համար: Համատեքստ օգտագործելիս դուք պետք է համոզվեք, որ սահմանում եք այն տվյալները, որոնք ցանկանում եք տրամադրել, և այդ տվյալները (մեր օրինակում օգտագործողը) փոխանցում եք ներսում տեղադրված բոլոր բաղադրիչ ծառին, ուստի սահմանեք նոր վիճակի փոփոխական օգտվող մատակարարի բաղադրիչի ներսում և վերջապես մենք զանգվածի ներսում փոխանցում ենք և՛ օգտվողին, և՛ setUser-ին որպես արժեք, որը մատակարարը կբացահայտի բաղադրիչի ծառին.

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

Մեկ այլ բան, որ մենք կարող ենք անել, մեր «անուն» վիճակի փոփոխականն է հավելվածի հիմնական բաղադրիչից տեղափոխել Auth համատեքստ և ցուցադրել այն ներդիր բաղադրիչներին.

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

Այժմ մնում է միայն մեր հավելվածը տեղադրել նույն AuthProvider բաղադրիչի մեջ, որը մենք նոր ենք արտահանել:

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

Քանի որ մենք ներկայացնում ենք երեխաների հենարանը Auth.Provider-ի ներսում, բոլոր տարրերը, որոնք տեղադրված են AuthProvider բաղադրիչի ներսում, այժմ կարող են սպառել արժեքային հենակետը, որը մենք փոխանցել ենք Auth.Provider-ին: Դա կարող է շփոթեցնող թվալ, բայց երբ փորձարկեք դրա հետ, փորձեք ապահովել և սպառել գլոբալ - Համատեքստ - վիճակ: Ի վերջո, դա ինձ համար իմաստ ունեցավ միայն Context API-ի հետ փորձարկումներից հետո:

Սպառում է Auth համատեքստը

Վերջնական քայլը պարզ է, մենք օգտագործում ենք «useContext» համատեքստի կեռը՝ մուտք գործելու այն արժեքը, որը տրամադրում է «Auth»-ի համատեքստի մատակարարը, որը մեր դեպքում օգտագործող և setUser պարունակող զանգվածն է, հետևյալ կոդում մենք կարող ենք օգտագործեք useContext՝ Navbar-ի ներսում Auth համատեքստը սպառելու համար: Դա հնարավոր է միայն այն պատճառով, որ Navbar-ը տեղադրված է App բաղադրիչի ներսում, և քանի որ AuthProvider-ը փաթաթված է App բաղադրիչի շուրջը, արժեքը կարող է օգտագործվել միայն useContext կեռիկի միջոցով: Մեկ այլ հիանալի գործիք React-ն ապահովում է առանց տուփի կառավարելու ցանկացած տվյալ, որը կարող է հասանելի լինել համաշխարհային մասշտաբով, ինչպես նաև շահարկել սպառողի ցանկացած բաղադրիչ:

./src/components/Navbar.jsx

export default function Navbar() {
 const [name, setName] = useContext(Auth);
 console.log(name)

 return (...)};

Ուշադրություն դարձրեք, թե ինչպես մենք այլևս չենք ընդունում որևէ հենարան Navbar() ֆունկցիոնալ բաղադրիչում: Մենք օգտագործում ենք useContext(Auth)՝ փոխարենը օգտագործելու Auth համատեքստը՝ գրավելով և՛ անունը, և՛ setName: Սա նաև նշանակում է, որ մենք այլևս կարիք չունենք հենակետը փոխանցել Navbar-ին.

./src/App.jsx

// ... //
return (
   <BrowserRouter>
     <div className="h-screen">
       <Navbar/> // no need to pass prop anymore
// ... //

Auth համատեքստի թարմացում

Մենք կարող ենք նաև օգտագործել տրամադրված setName ֆունկցիան՝ «անուն» վիճակի փոփոխականը շահարկելու համար.

./src/pages/EditUser.jsx

export default function EditUser() { // no need to accept props anymore
 const [name, setName] = useContext(Auth); // grabbing the name and setName variables from Auth context
 console.log("Rendering: EditUser");

Ներկայացնում ենք useReducer() կեռը

Նախորդ օրինակում մենք օգտագործեցինք համատեքստային API՝ կառավարելու և կիսելու մեր վիճակը բաղադրիչի ծառի տակ, դուք կարող եք նկատել, որ մենք դեռ օգտագործում ենք useState որպես մեր տրամաբանության հիմք, և դա հիմնականում լավ է, քանի որ մեր վիճակը դեռ պարզ օբյեկտ է: այս պահին, բայց եթե մենք ընդլայնելու ենք մեր իսկորոշման հոսքի հնարավորությունները, մենք անպայման պետք է պահենք ավելին, քան պարզապես ներկայումս մուտք գործած օգտատիրոջ էլ. useState-ի օգտագործումը բարդ վիճակով, բարեբախտաբար, React-ը լուծում է այս խնդիրը՝ տալով useState-ին այլընտրանք բարդ վիճակի կառավարման համար. մուտքագրեք useReducer:

useReducer-ը կարելի է համարել useState-ի ընդհանրացված տարբերակ, այն պահանջում է երկու պարամետր՝ ռեդուկտոր ֆունկցիա և սկզբնական վիճակ:

Նախնական վիճակի համար ոչ մի հետաքրքիր բան նշելու համար, կախարդանքը տեղի է ունենում ռեդուկտորի ֆունկցիայի ներսում. այն ստուգում է կատարված գործողության տեսակը, և կախված այդ գործողությունից, ռեդուկտորը կորոշի, թե ինչ թարմացումներ կիրառել վիճակի վրա և կվերադարձնի իր նոր արժեքը: .

Նայելով ստորև բերված կոդը՝ կրճատիչ ֆունկցիան ունի երկու հնարավոր գործողության տեսակ.

  • «LOGIN». այս դեպքում օգտատիրոջ վիճակը կթարմացվի նոր օգտատիրոջ հավատարմագրերով, որոնք տրամադրվում են գործողության բեռնվածքի ներսում:

  • «LOGOUT». այս դեպքում օգտագործողի վիճակը կհեռացվի տեղական պահեստից և կվերադարձվի զրոյականի:

Կարևոր է նշել, որ գործողության օբյեկտը պարունակում է և՛ տիպի դաշտ, որը որոշում է, թե ինչ տրամաբանություն կիրառել, և՛ կամընտիր ծանրաբեռնվածության դաշտ՝ տրամադրելու տվյալներ, որոնք անհրաժեշտ են այդ տրամաբանությունը կիրառելու համար:

Ի վերջո, useReducer hook-ը վերադարձնում է ընթացիկ վիճակը և առաքման գործառույթը, որը մենք օգտագործում ենք ռեդուկտորին գործողություն փոխանցելու համար:

Մնացած տրամաբանության համար դա նույնական է նախորդ օրինակին.

./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>
  );
};

Գործողությունների առաքում setState կարգավորիչ ֆունկցիան օգտագործելու փոխարեն, օրինակ՝ setName –

Ինչպես հենց նոր նշեցինք, մենք օգտագործում ենք դիսպետչերական ֆունկցիան՝ ռեդուկտորին գործողություն փոխանցելու համար, հետևյալ կոդի մեջ մենք նախաձեռնում ենք LOGIN գործողություն և տրամադրում ենք օգտվողի էլ. օգտագործողի կարգավիճակին բաժանորդագրված բոլոր բաղադրիչների վերարտադրում: Կարևոր է նշել, որ վերարտադրումը կգործարկվի միայն այն դեպքում, եթե վիճակի իրական փոփոխություն տեղի ունենա, և ոչ մի վերարտադրում, եթե ռեդուկտորը վերադարձնի նույն նախկին վիճակը:

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 (...)};

Օգտագործողի մուտք

JWT Local storage

Pro Tip

Ուշադրություն դարձրեք, թե ինչպես է օգտվողի օբյեկտը, որը մենք ստանում ենք հաջող մուտքից հետո, այժմ պահվում է localStorage-ում:

Պատվերով կեռիկ մուտք գործելու համար

Այժմ, երբ մենք լավ տիրապետում ենք useReducer-ին, մենք կարող ենք հետագայում ներառել մեր մուտքի և ելքի տրամաբանությունը իրենց առանձին հատուկ կեռիկների մեջ, մեկ զանգով դեպի Մուտքի կեռիկ, մենք կարող ենք կատարել API-ի զանգ դեպի մուտքի երթուղի, առբերել նոր օգտվողին: հավատարմագրերը և պահեք դրանք տեղական պահեստում, ուղարկեք LOGIN զանգ՝ օգտատիրոջ վիճակը թարմացնելու համար, այս ամենը սխալների հետ աշխատելիս.

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

Նշում. ընթերցողների շրջանում React-ի ավելի առաջադեմ օգտվողների համար կարող եք մտածել, թե ինչու մենք չօգտագործեցինք useReducer-ի ծույլ սկզբնավորման հնարավորությունը օգտվողի հավատարմագրերը ստանալու համար, useReducer-ն ընդունում է երրորդ ընտրովի պարամետրը, որը կոչվում է init ֆունկցիա, այս ֆունկցիան օգտագործվում է այն դեպքում, երբ մենք պետք է որոշակի տրամաբանություն կիրառենք, նախքան մենք կարողանանք ստանալ պետության սկզբնական արժեքը, պատճառը, որ մենք դա չընտրեցինք, մտահոգությունների տարանջատման պարզ խնդիրն է, ծածկագիրը այս կերպ ավելի պարզ է հասկանալի և արդյունքում ավելի հեշտ է պահպանել: .

Մուտք էջ

Ահա, թե ինչպես է մեր Մուտք էջի վերևի տեսքը այն բանից հետո, երբ օգտագործում եք useLogin() կեռը՝ մուտքի ֆունկցիան հանելու համար և մուտք գործելու գործառույթը օգտատիրոջ կողմից ներկայացված հավատարմագրերով կանչելուց հետո.

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

Մենք նաև անջատում ենք Submit ֆունկցիան, երբ օգտատերը ներկայացնում է ձևը.

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

Եվ նույնականացման ցանկացած սխալ, որը մենք ստանում ենք մեր հետին պլանից.

{
  error && <span className="text-red-500 p-2">{error.message}</span>;
}

Rendering Errors

Օգտվողի վիճակի պահպանում

Դուք կարող եք մտածել, թե ինչու է մեզ անհրաժեշտ օգտատիրոջ օբյեկտը պահել localStorage-ում, պարզ ասած, մենք ցանկանում ենք օգտատիրոջը մուտք գործել այնքան ժամանակ, քանի դեռ նշանի ժամկետը լրացած չէ: LocalStorage-ի օգտագործումը JSON-ի բիթերը պահելու հիանալի միջոց է, ինչպես մեր օրինակում: Ուշադրություն դարձրեք, թե ինչպես է նահանգը ջնջվում, եթե էջը թարմացնեք մուտք գործելուց հետո: Դա կարելի է հեշտությամբ լուծել՝ օգտագործելով useEffect կեռիկը՝ ստուգելու համար, թե արդյոք մենք ունենք պահպանված օգտատիրոջ օբյեկտ localStorage-ում, եթե կա այդպիսին, մենք ավտոմատ կերպով մուտք ենք գործում օգտատիրոջը.

// ... ... //
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>
  );
};

Պատվերով կեռիկներ Դուրս գալու համար

Նույնը վերաբերում է «Դուրս գալու» կեռին, այստեղ մենք ուղարկում ենք LOGOUT գործողություն՝ ընթացիկ օգտատիրոջ հավատարմագրերը հեռացնելու համար թե՛ նահանգից, թե՛ տեղական պահեստից.

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

Օգտատիրոջ ելք

Օգտատիրոջը դուրս գալու համար եկեք կտտոցով իրադարձություն ավելացնենք UserDropdown.jsx-ում հայտնաբերված «Ելք» կոճակին և համապատասխանաբար կարգավորենք այն.

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

React երթուղիների պաշտպանություն

Մեր հավելվածի իրականացման վերջին քայլը օգտատերերի գլոբալ վիճակի օգտագործումն է՝ օգտատերերի նավիգացիան վերահսկելու համար, արագ հիշեցում այն ​​մասին, թե ինչ վարքագծի պետք է հասնենք. սկզբում օգտատերը ողջունվում է մուտքի էջով, այդ պահից օգտվողը կարող է մուտք գործել միայն գլխավոր էջ: Հաջող մուտքից հետո, նմանապես, օգտատերը դուրս գալուց հետո կուղղորդվի մուտքի էջ:

Մենք դրան հասնում ենք react-router-dom գրադարանի օգնությամբ՝ սահմանելով 2 երթուղիներ՝ «/» և «/login», մենք վերահսկում ենք, թե որ բաղադրիչը պետք է ցուցադրվի յուրաքանչյուր երթուղում՝ օգտագործելով գլոբալ վավերացման վիճակը:, վավերացումը զրոյական դարձնելը ներկայացնում է չվավերացված օգտվող և հակառակը՝

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

Recap դիագրամ

Diagram

Ամփոփում

Այս հոդվածում մենք փորձեցինք անդրադառնալ նույնականացման աշխատանքային հոսքի համար պետական ​​կառավարում իրականացնելու պարզ, բայց շատ տարածված օգտագործման դեպքին՝ անցնելով տարբեր մոտեցումների, յուրաքանչյուրի հիմքում ընկած հիմնավորման և դրանց փոխզիջումների միջով: Պետական ​​կառավարումը հաճախորդի կողմի շրջանակներում և մասնավորապես React-ում ամենաքննարկվող թեմաներից մեկն է frontend համայնքում, պարզապես այն պատճառով, որ այն կարող է կա՛մ կատարելագործել, կա՛մ խաթարել ձեր հավելվածի կատարումն ու մասշտաբայինությունը: Տարբեր տեխնիկաների, օրինաչափությունների, գրադարանների և գործիքների հսկայական քանակությունը, որոնք փորձում են լուծել պետական ​​կառավարման այս խնդիրը, ճնշող է, մեր նպատակն էր ձեզ լավ պատկերացում տալ պրակտիկայի մասին, որպեսզի կարողանաք այն կիրառել ձեր սեփական կիրառման մեջ, ավելի առաջադեմ համար: Պետական ​​կառավարման տեխնիկան և օրինաչափությունները ավելի բարդ հավելվածներում, տեսեք մեր հաջորդ հոդվածը, որտեղ մենք մտնում ենք React-ի մասշտաբային պետական ​​կառավարման ռեդուքս:

Շուտով

ContextAPI vs Redux Toolkit

Redux-ը երկար տարիներ ընդունվել է համայնքում մինչև Redux գործիքակազմը, փաստորեն, RTK-ն ներկայացվել է որպես մեկնարկային ձևանմուշ՝ նոր հավելվածներում redux-ի պետական ​​կառավարումը բեռնելու համար (նրա սկզբնական անունը եղել է «redux-starter-kit» 2019 թվականի հոկտեմբերին), չնայած. Այսօր Redux-ի սպասարկողների և համայնքի միջև ընդհանուր համաձայնություն կա, որ Redux-ի գործիքակազմը redux-ի հետ աշխատելու վավերական միջոց է: RTX-ը վերացում է Redux-ի շատ տրամաբանություն, ինչը հեշտացնում է այն օգտագործելը, շատ ավելի քիչ խոսուն, և խրախուսում է մշակողներին. հետևեք լավագույն փորձին, երկուսի միջև հիմնական տարբերությունն այն է, որ Redux-ը ստեղծվել է կարծիք չունենալու համար՝ ապահովելով նվազագույն API և ակնկալելով, որ մշակողները կատարեն ծանրաբեռնվածության մեծ մասը՝ գրելով իրենց սեփական գրադարանները ընդհանուր առաջադրանքների համար և գործ ունենալով կոդի կառուցվածքի հետ: Սա հանգեցրեց զարգացման դանդաղ ժամանակի և խառնաշփոթ կոդի, Redux գործիքակազմն ավելացվեց որպես վերացականության լրացուցիչ շերտ, որը թույլ չի տալիս ծրագրավորողներին ընկնել իր ընդհանուր որոգայթների մեջ, ավելի շատ պատկերացումների և դրա պահպանողների հիմնավորումների համար դիմեք պաշտոնական փաստաթղթերին այստեղ:


Career Services background pattern

Կարիերայի ծառայություններ

Contact Section background image

Եկեք մնանք կապի մեջ

Code Labs Academy © 2024 Բոլոր իրավունքները պաշտպանված են.