Кіраванне станам у React: практычны прыклад

рэагаваць
кіраванне станам
кантэкст API
Кіраванне станам у React: практычны прыклад cover image

React - гэта стандартная структура для стварэння дынамічных кліенцкіх прыкладанняў для многіх распрацоўшчыкаў. Дынамічны характар ​​гэтых прыкладанняў абумоўлены гнуткасцю і пашыраным спісам магчымасцей і функцый, якія даступныя на баку кліента, што дазволіла распрацоўшчыкам ствараць паўнавартасныя прыкладанні, якія загружаюцца ў браўзеры за лічаныя секунды, што не было магчыма (або вельмі грувастка) у дні статычнага Інтэрнэту.

З такім пашырэннем магчымасцей з'явілася канцэпцыя кіравання станам, па меры ўскладнення кліенцкіх прыкладанняў расце неабходнасць захавання лакальнага стану, якая сама па сабе становіцца вузкім месцам, калі з ёй не звяртацца правільна і не ўлічваць маштабаванасць.

Гэтае пытанне разглядалася многімі фрэймворкамі з прытрымліваннем розных падыходаў і засяроджваннем увагі на розных наборах падпраблем, таму важна мець высокі ўзровень разумення экасістэмы абранага фрэймворка, каб ацаніць патрэбы кожнага прыкладання і выкарыстоўваць правільны падыход у адпаведнасці з імі метрыкі. Гэты артыкул дасць вам кароткі агляд агульных праблем кіравання станам і паспрабуе прадставіць розныя падыходы (useState, Context API) у якасці адказу на іх. Нягледзячы на ​​тое, што ў гэтым артыкуле будзе прадстаўлена некалькі рашэнняў, ён будзе сканцэнтраваны толькі на праблемах у меншым маштабе, мы разгледзім больш складаныя тэмы ў наступных артыкулах.

Рабочы працэс аўтэнтыфікацыі

Прадэманстраваны ў артыкуле код можна знайсці тут.

Спасылка на папярэдні прагляд у жывым эфіры даступная тут.

Разгледзім выпадак, калі мы рэалізуем працэс аўтэнтыфікацыі для прыкладання React.

User Login

Як паказана ў GIF-файле вышэй, мы хочам дазволіць карыстальнікам уваходзіць або рэгістравацца ў нашым дадатку, выкарыстоўваючы ўліковыя дадзеныя. Калі былі прадастаўлены сапраўдныя ўліковыя дадзеныя, карыстальнік будзе ўваходзіць у сістэму, праграма аўтаматычна пяройдзе на галоўную старонку, і карыстальнік зможа перайсці да выкарыстання праграмы.

Сапраўды гэтак жа, калі карыстальнік выходзіць з сістэмы, рэсурсы галоўнай старонкі будуць абаронены за ўваходам у сістэму, старонка ўваходу будзе адзінай старонкай, даступнай для карыстальніка.

Разважаючы пра гэты працоўны працэс з пункту гледжання рэалізацыі, у нас будзе асноўны кампанент пад назвай App, кампанент App будзе перанакіроўваць карыстальніка на адну з дзвюх старонак: Home або Login, бягучы стан карыстальніка (увайшоў у сістэму, выйшаў з сістэмы) будзе вызначаць, якая старонкі, на якую перанакіроўваецца карыстальнік, змяненне бягучага стану карыстальніка (напрыклад, змена ўваходу ў сістэму на выхад з сістэмы) павінна выклікаць імгненнае перанакіраванне на адпаведную старонку.

State

Як паказана на малюнку вышэй, мы хочам, каб кампанент App улічваў бягучы стан і адлюстроўваў толькі адну з дзвюх старонак - Home або Login - на аснове гэтага бягучага стану.

Калі карыстальнік нулявы, гэта азначае, што ў нас няма аўтэнтыфікаванага карыстальніка, таму мы аўтаматычна пераходзім на старонку ўваходу і абараняем хатнюю старонку з дапамогай умоўнага візуалізацыі. Калі карыстальнік існуе, мы робім наадварот.

Цяпер, калі ў нас ёсць дакладнае разуменне таго, што трэба рэалізаваць, давайце вывучым некалькі варыянтаў, але спачатку давайце наладзім наш праект React,

Рэпазітар праекта змяшчае ўзор бэкэнд-праграмы, які мы будзем выкарыстоўваць для рэалізацыі інтэрфейсу (мы не будзем удавацца ў яго, таму што гэта не галоўная ўвага тут, але код быў наўмысна зроблены простым, так што ў вас не будзе цяжкасці з ім )

Мы пачынаем са стварэння наступных старонак і кампанентаў:

  • Галоўная старонка

  • Старонка ўваходу

  • старонка EditUser

  • Кампанент Navbar

  • Кампанент UserDropdown

Праграма React з некалькімі старонкамі патрабуе належнай навігацыі, для гэтага мы можам выкарыстоўваць react-router-dom для стварэння глабальнага кантэксту маршрутызатара браўзера і рэгістрацыі розных маршрутаў React.


yarn add react-router-dom

Мы ведаем, што многія чытачы аддаюць перавагу прытрымлівацца падручнікаў, таму вось стартавы шаблон, які дапаможа вам паскорыць. Гэтая пачатковая галіна выкарыстоўвае DaisyUI для прадвызначаных кампанентаў TailwindCSS JSX. Ён уключае ўсе кампаненты, старонкі і ўжо наладжаны маршрутызатар. Калі вы збіраецеся прытрымлівацца гэтага падручніка, ствараючы ўвесь працэс аўтэнтыфікацыі самастойна, выконваючы простыя крокі, пачніце спачатку з разгалінавання рэпазітара. Пасля таго, як вы разгалужылі рэпазітар, клануйце яго і пачніце з старт-тутгалінкі:


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

Пасля таго, як вы выцягнеце галінку start-here:

  • Адкрыйце праект у абраным рэдактары кода

  • Змяніць каталог на frontend/

  • Усталяваць залежнасці: пража

  • Запусціце сервер распрацоўкі: yarn dev·

Папярэдні прагляд павінен выглядаць прыкладна так:

Changing user name

Імя, адлюстраванае ў панэлі навігацыі (верхні правы бок), з'яўляецца зменнай стану, вызначанай у галоўным кампаненце праграмы. Адна і тая ж зменная перадаецца і на панэль навігацыі, і на галоўную старонку. Простая форма, выкарыстаная вышэй, фактычна абнаўляе зменную стану «name» з кампанента EditPage.

Два падыходы, прадстаўленыя ніжэй, будуць разгледжаны дэталі рэалізацыі:

Першы падыход: useState

useState()

useState - адзін з найбольш часта выкарыстоўваных хукаў React, ён дазваляе ствараць і змяняць стан у функцыянальным кампаненце React. Кампанент useState мае вельмі простую рэалізацыю і просты ў выкарыстанні: каб стварыць новы стан, вам трэба выклікаць useState з пачатковым значэннем вашага стану, і хук useState верне масіў, які змяшчае дзве зменныя: першая - гэта стан зменная, якую вы можаце выкарыстоўваць для спасылкі на свой стан, а другая функцыя, якую вы выкарыстоўваеце для змены значэння стану: даволі проста.

Як наконт таго, каб мы ўбачылі гэта ў дзеянні? Імя, адлюстраванае ў панэлі навігацыі (верхні правы бок), з'яўляецца зменнай стану, вызначанай у галоўным кампаненце праграмы. Адна і тая ж зменная перадаецца і на панэль навігацыі, і на галоўную старонку. Простая форма, выкарыстаная вышэй, фактычна абнаўляе зменную стану «name» з кампанента 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 (...)};

Рэквізіт

Рэквізіты з'яўляюцца адным з асноўных будаўнічых блокаў кампанента React, канцэптуальна, калі вы разглядаеце функцыянальны кампанент React як функцыю Javascript, то рэквізіты - гэта не больш, чым параметры функцыі, спалучэнне рэквізітаў і хука useState можа даць вам трывалую структуру для кіравання станам простага прыкладання React.

Рэквізіты React перадаюцца ў якасці атрыбутаў карыстальніцкім кампанентам. Атрыбуты, перададзеныя ў якасці рэквізітаў, могуць быць дэструктураваны з аб'екта рэквізітаў пры прыняцці яго ў якасці аргумента, падобна таму:

Праходжанне рэквізіту

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

Рэквізіты могуць быць прыняты і выкарыстаны ўнутры функцыянальнага кампанента, падобнага да звычайных аргументаў функцыі. Менавіта таму, што «імя» перадаецца ў якасці апорнага кампанента Home, мы можам адлюстраваць яго ў тым жа кампаненце. У наступным прыкладзе мы прымаем перададзены прап, выкарыстоўваючы сінтаксіс дэструктурызацыі, каб атрымаць уласцівасць name з аб'екта 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>
... ...

Прафесійная парада

Адкрыйце кансоль браўзера і заўважце, як усе кампаненты, якія выкарыстоўваюць прапіў «імя», паўторна адлюстроўваюцца пры змене стану. Пры маніпуляцыі са зменнай стану React захавае наступны стан, паўторна візуалізуе ваш кампанент з новымі значэннямі і абновіць карыстацкі інтэрфейс.

Components Re-rendering

Недахопы useState

Рэквізітнае свідраванне

Свідраванне рэквізітаў - гэта тэрмін для абазначэння іерархіі кампанентаў, дзе набор кампанентаў патрабуе пэўных рэквізітаў, прадастаўленых бацькоўскім кампанентам. Звычайнае абыходнае рашэнне, якое звычайна выкарыстоўваюць нявопытныя распрацоўшчыкі, заключаецца ў перадачы гэтых рэквізітаў па ўсёй ланцужку кампанентаў, праблема з гэтым падыход заключаецца ў тым, што змяненне любога з гэтых рэквізітаў выкліча паўторны рэндэрынг усяго ланцужка кампанентаў, фактычна запавольваючы працу ўсяго прыкладання ў выніку гэтых непатрэбных візуалізацый, кампанентаў у сярэдзіне ланцужка, якія не патрабуюць гэтых рэквізітаў выступаць у якасці асяроддзя для перадачы рэквізіту.

Прыклад:

  • Пераменная стану была вызначана ў галоўным кампаненце праграмы з дапамогай хука 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 і рэквізітаў у якасці адзінага сродкі кіравання станам у дадатку React база кода можа хутка ўскладняцца, даводзіцца кіраваць дзесяткамі ці сотнямі зменных стану, якія могуць быць дублікатамі адна адной, раскіданымі па розных файлах і кампаненты могуць быць вельмі страшнымі, любое змяненне дадзенай зменнай стану запатрабуе ўважлівага разгляду залежнасцяў паміж кампанентамі, каб пазбегнуць патэнцыйнага дадатковага паўторнага рэндэрынгу ў і без таго павольным дадатку.

Другі падыход: кантэкстны API

Кантэкстны API - гэта спроба React вырашыць недахопы выкарыстання толькі рэквізітаў і useState для кіравання станам, у прыватнасці, кантэкстны API з'яўляецца адказам на згаданую раней праблему адносна неабходнасці перадачы рэквізітаў па ўсім дрэве кампанентаў. З выкарыстаннем кантэксту вы можаце вызначыць стан для даных, якія вы лічыце глабальнымі, і атрымаць доступ да іх стану з любой кропкі дрэва кампанентаў: больш не трэба свідраваць.

Важна адзначыць, што кантэкстны API першапачаткова быў задуманы для вырашэння праблемы глабальнага абмену данымі, такімі як тэмы карыстальніцкага інтэрфейсу, інфармацыя пра аўтэнтыфікацыю, якая з'яўляецца нашым варыянтам выкарыстання, мовы і г.д.), для іншых тыпаў даных, якімі трэба абагульваць сярод больш чым аднаго кампанента, але не абавязкова з'яўляецца глабальным для ўсяго прыкладання, кантэкст можа быць не лепшым варыянтам, у залежнасці ад варыянту выкарыстання, вы можаце разгледзець іншыя метады, такія як кампазіцыя кампанентаў, што выходзіць за рамкі гэтага артыкула.

Пераключэнне ў кантэкст

Стварэнне кантэксту аўтарызацыі

createContext просты, ён стварае новую кантэкстную зменную, прымае адзіны неабавязковы параметр: значэнне кантэкстнай зменнай па змаўчанні.

Давайце паглядзім на гэта ў дзеянні, спачатку стварыце новую папку «contexts» і новы файл у ёй «Auth.jsx». Каб стварыць новы кантэкст, нам трэба выклікаць функцыю createContext(), прысвоіць вернутае значэнне новай зменнай Auth, якая будзе экспартавана наступным чынам:

./src/contexts/Auth.jsx

import { createContext } from "react";

export const Auth = createContext();

Увядзіце кантэкст аўтэнтыфікацыі

Цяпер нам трэба выставіць кантэкстную зменную "Auth", якую мы стварылі раней, каб дасягнуць гэтага, мы выкарыстоўваем пастаўшчыка кантэксту, пастаўшчык кантэксту - гэта кампанент, які мае апору "значэнне", мы можам выкарыстаць гэтую апору для сумеснага выкарыстання значэння - імя – па ўсім дрэве кампанентаў, абгортваючы дрэва кампанентаў кампанентам пастаўшчыка, мы робім гэтае значэнне даступным з любой кропкі дрэва кампанентаў без неабходнасці перадачы гэтага апорнага кампанента кожнаму даччынаму кампаненту паасобку.

У нашым прыкладзе аўтэнтыфікацыі нам патрэбна зменная для захоўвання карыстальніцкага аб'екта і нейкі сетэр для маніпулявання гэтай зменнай стану. Гэта ідэальны варыянт выкарыстання useState. Калі вы выкарыстоўваеце кантэкст, вы павінны пераканацца, што вы вызначаеце даныя, якія вы хочаце прадаставіць, і перадаеце гэтыя даныя (карыстальніка ў нашым прыкладзе) ва ўсё дрэва кампанентаў, укладзенае ўнутры, таму вызначыце новую зменную стану user у кампаненце пастаўшчыка і нарэшце, мы перадаем як user, так і 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>;
};

Іншая рэч, якую мы можам зрабіць, гэта перамясціць зменную стану «імя» з асноўнага кампанента праграмы ў кантэкст аўтэнтыфікацыі і прадставіць яе для ўкладзеных кампанентаў:

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.

Выкарыстанне кантэксту аўтэнтыфікацыі

Апошні крок просты: мы выкарыстоўваем хук кантэксту "useContext", каб атрымаць доступ да значэння, якое прадастаўляе правайдэр кантэксту "Auth", якое ў нашым выпадку з'яўляецца масівам, які змяшчае карыстальніка і setUser, у наступным кодзе мы можам выкарыстоўвайце useContext, каб выкарыстоўваць кантэкст аўтэнтыфікацыі ўнутры панэлі навігацыі. Гэта магчыма толькі таму, што 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 замест гэтага, захопліваючы і name, і setName. Гэта таксама азначае, што нам больш не трэба перадаваць прап у Navbar:

./src/App.jsx

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

Абнаўленне кантэксту аўтэнтыфікацыі

Мы таксама можам выкарыстоўваць прадстаўленую функцыю setName для маніпулявання зменнай стану «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");

Прадстаўляем хук useReducer().

У папярэднім прыкладзе мы выкарыстоўвалі кантэкстны API для кіравання і сумеснага выкарыстання нашага стану ўнізе па дрэве кампанентаў, вы маглі заўважыць, што мы ўсё яшчэ выкарыстоўваем useState у якасці асновы нашай логікі, і гэта ў асноўным нармальна, бо наш стан па-ранейшаму з'яўляецца простым аб'ектам на дадзены момант, але калі мы хочам пашырыць магчымасці нашага патоку аўтэнтыфікацыі, нам абавязкова трэба будзе захоўваць больш, чым проста электронную пошту карыстальніка, які ў цяперашні час увайшоў у сістэму, і тут мы вяртаемся да абмежаванняў, якія мы накладалі раней у дачыненні да выкарыстанне useState са складаным станам, на шчасце, React вырашае гэтую праблему, забяспечваючы альтэрнатыву useState для кіравання складаным станам: увядзіце useReducer.

useReducer можна разглядаць як абагульненую версію useState, яна прымае два параметры: функцыю рэдуктара і пачатковы стан.

Нічога цікавага не варта адзначыць у пачатковым стане, магія адбываецца ўнутры функцыі рэдуктара: яна правярае тып дзеяння, якое адбылося, і ў залежнасці ад гэтага дзеяння рэдуктар вызначыць, якія абнаўленні прымяніць да стану, і верне яго новае значэнне .

Гледзячы на ​​код ніжэй, функцыя рэдуктара мае два магчымыя тыпы дзеянняў:

  • "LOGIN": у гэтым выпадку стан карыстальніка будзе абноўлены новымі ўліковымі дадзенымі карыстальніка, прадстаўленымі ўнутры карыснай нагрузкі дзеянняў.

  • "ВЫХОД": у гэтым выпадку стан карыстальніка будзе выдалены з лакальнага сховішча і вернецца да нуля.

Важна адзначыць, што аб'ект дзеяння змяшчае як поле тыпу, якое вызначае, якую логіку прымяняць, так і дадатковае поле карыснай нагрузкі для прадастаўлення даных, неабходных для прымянення гэтай логікі.

Нарэшце, хук useReducer вяртае бягучы стан і дыспетчарскую функцыю, якую мы выкарыстоўваем для перадачы дзеяння рэдуктару.

Для астатняй логікі гэта ідэнтычна папярэдняму прыкладу:

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

Прафесійная парада

Звярніце ўвагу, як аб'ект карыстальніка, які мы атрымліваем пасля паспяховага ўваходу, цяпер захоўваецца ў localStorage.

Карыстальніцкі хук для ўваходу

Цяпер, калі мы добра валодаем useReducer, мы можам далей інкапсуляваць нашу логіку ўваходу і выхаду з сістэмы ў іх уласныя асобныя карыстальніцкія хукі, з дапамогай аднаго выкліку хука Login мы можам апрацоўваць выклік 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 прымае трэці дадатковы параметр, які называецца функцыяй ініцыялізацыі, гэтая функцыя выкарыстоўваецца ў выпадку, калі нам трэба прымяніць некаторую логіку, перш чым мы зможам атрымаць пачатковае значэнне стану, прычына, па якой мы не выбралі гэта, заключаецца ў простым падзеле праблем, код такім чынам прасцей зразумець і, як следства, прасцей падтрымліваць .

Старонка ўваходу

Вось як выглядае верхняя частка нашай старонкі ўваходу пасля выкарыстання хука 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 (...)
// ... ... //

Мы таксама адключаем функцыю адпраўкі, калі карыстальнік адпраўляе форму:

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

Карыстальніцкія хукі для выхаду з сістэмы

Тое ж самае адносіцца і да перахопу выхаду, тут мы адпраўляем дзеянне ВЫХОД, каб выдаліць уліковыя дадзеныя бягучага карыстальніка як са стану, так і з лакальнага сховішча:

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

Паўторная дыяграма

Diagram

Падвядзенне вынікаў

У гэтым артыкуле мы паспрабавалі разабрацца з простым, але вельмі распаўсюджаным варыянтам выкарыстання кіравання станам для працоўнага працэсу аўтэнтыфікацыі, разглядаючы розныя падыходы, абгрунтаванне кожнага з іх і іх кампрамісы. Кіраванне станам у фрэймворках на баку кліента і, у прыватнасці, у React, з'яўляецца адной з найбольш абмяркоўваемых тэм у інтэрфейснай супольнасці проста таму, што яно можа альбо палепшыць, альбо парушыць прадукцыйнасць і маштабаванасць вашага прыкладання. Велізарная колькасць розных метадаў, шаблонаў, бібліятэк і інструментаў, якія спрабуюць вырашыць гэтую праблему дзяржаўнага кіравання, велізарная, наша мэта складалася ў тым, каб даць вам дакладнае разуменне практыкі, каб вы маглі рэалізаваць гэта ў сваім уласным дадатку для больш прасунутых метады і шаблоны кіравання станам у больш складаных праграмах, азнаёмцеся з нашым наступным артыкулам, дзе мы разбярэмся з рэдукцыяй для маштабаванага кіравання станам у React.

Хутка

ContextAPI супраць 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 Усе правы абароненыя.