React'te Durum Yönetimi: Pratik Bir Örnek

tepki
durum yönetimi
bağlam API'si
React'te Durum Yönetimi: Pratik Bir Örnek cover image

React, birçok geliştirici için dinamik istemci tarafı uygulamalar oluşturmak için başvurulacak çerçevedir. Bu uygulamaların dinamik doğası, istemci tarafında mümkün olan esneklikten ve genişletilmiş yetenek ve özellikler listesinden gelir; bu, geliştiricilerin tarayıcıya saniyeler içinde yüklenen tam teşekküllü uygulamalar oluşturmasına olanak tanır; bu, şimdiye kadar görülmemiş bir başarıdır. Statik web günlerinde mümkün (veya çok hantal).

Olasılıklardaki bu genişlemeyle birlikte durumu yönetme kavramı ortaya çıktı; istemci tarafı uygulamalarda karmaşıklık arttıkça, yerel durumu koruma ihtiyacı, doğru şekilde ele alınmazsa ve ölçeklenebilirlik akılda tutularak tasarlanmazsa, başlı başına bir darboğaz haline gelecek şekilde büyür.

Bu konu, farklı yaklaşımları takip eden ve farklı alt problem gruplarına odaklanan birçok çerçeve tarafından ele alınmıştır; bu nedenle, her bir uygulamanın ihtiyaçlarını değerlendirmek ve bunları takip eden doğru yaklaşımı uygulamak için tercih edilen çerçevenin ekosistemi hakkında yüksek düzeyde bir anlayışa sahip olmak önemlidir. Metrikler. Bu makale size genel durum yönetimi sorunlarına kısa bir genel bakış sunacak ve buna yanıt olarak farklı yaklaşımları (useState, Context API) tanıtmaya çalışacaktır. Bu makale birden fazla çözüm sunacak olsa da, yalnızca daha küçük ölçekteki zorluklara odaklanacak, gelecek makalelerde daha ileri konulara değineceğiz.

Kimlik Doğrulama İş Akışı

Makale boyunca gösterilen kodu burada bulabilirsiniz.

Canlı önizleme bağlantısına buradan erişilebilir.

Bir React uygulaması için kimlik doğrulama sürecini uyguladığımız durumu düşünün.

User Login

Yukarıdaki GIF'te gösterildiği gibi, kullanıcıların kimlik bilgilerini kullanarak uygulamamıza giriş yapmasına veya kaydolmasına izin vermek istiyoruz. Geçerli kimlik bilgilerinin sağlanması durumunda kullanıcı oturum açacak, uygulama otomatik olarak ana sayfaya gidecek ve kullanıcı uygulamayı kullanmaya devam edebilecektir.

Benzer şekilde, eğer kullanıcı çıkış yaparsa, girişin arkasında ana sayfa kaynakları korunacak, giriş sayfası kullanıcının erişebildiği tek sayfa olacaktır.

Bu iş akışını uygulama açısından düşünürsek, Uygulama adında bir ana bileşenimiz olur, Uygulama bileşeni kullanıcıyı iki sayfadan birine yönlendirir: Ana Sayfa veya Giriş, kullanıcının mevcut durumu (oturum açmış, oturumu kapatmış) hangisini belirleyecektir? Kullanıcının yönlendirildiği sayfada, kullanıcının mevcut durumundaki bir değişiklik (örneğin, giriş yapmış durumdan çıkış yapmış duruma geçmek) ilgili sayfaya anında yeniden yönlendirmeyi tetiklemelidir.

State

Yukarıdaki çizimde gösterildiği gibi, Uygulama bileşeninin geçerli durumu dikkate almasını ve bu geçerli duruma göre iki sayfadan yalnızca birini (Ana Sayfa veya Oturum Açma) oluşturmasını istiyoruz.

Kullanıcı null ise bu, kimliği doğrulanmış bir kullanıcımızın olmadığı anlamına gelir, bu nedenle giriş sayfasına otomatik olarak gideriz ve koşullu oluşturmayı kullanarak ana sayfayı koruruz. Kullanıcı varsa tam tersini yaparız.

Artık neyin uygulanması gerektiğine dair sağlam bir anlayışa sahip olduğumuza göre, birkaç seçeneği inceleyelim, ancak önce React projemizi kuralım.

Proje deposu, ön uç tarafını uygulamak için kullanacağımız örnek bir arka uç uygulamasını içerir (burada ana odak noktası bu olmadığı için buna girmeyeceğiz, ancak kod kasıtlı olarak basit tutuldu, böylece zor anlar yaşamazsınız) )

Aşağıdaki sayfaları ve bileşenleri oluşturarak başlıyoruz:

  • Ana Sayfa

  • Giriş Sayfası

  • Kullanıcı Sayfasını Düzenle

  • Gezinti Çubuğu Bileşeni

  • UserDropdown Bileşeni

Birden fazla sayfaya sahip bir React uygulamasının düzgün bir navigasyona ihtiyacı vardır; bunun için, global bir tarayıcı yönlendirici bağlamı oluşturmak ve farklı React rotalarını kaydetmek için react-router-dom'u kullanabiliriz.


yarn add react-router-dom

Pek çok okuyucunun öğreticilerle birlikte takip etmeyi tercih ettiğini biliyoruz, bu yüzden size hız kazandıracak başlangıç ​​şablonunu burada bulabilirsiniz. Bu başlangıç ​​dalı, önceden tanımlanmış TailwindCSS JSX bileşenleri için DaisyUI'yi kullanır. Halihazırda kurulmuş olan tüm bileşenleri, sayfaları ve yönlendiriciyi içerir. Bu öğreticiyi takip etmeyi ve basit adımları izleyerek tüm kimlik doğrulama akışını kendiniz oluşturmayı düşünüyorsanız, önce depoyu çatallayarak başlayın. Depoyu çatalladıktan sonra klonlayın ve start-heredalındanbaşlayın:


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

Buradan başla dalını çektiğinizde:

  • Projeyi tercih ettiğiniz kod düzenleyiciyle açın

  • Dizini ön uç/ olarak değiştirin

  • Bağımlılıkları yükleyin: iplik

  • Bir geliştirme sunucusu başlatın: iplik geliştirme·

Önizleme şöyle görünmelidir:

Changing user name

Gezinme Çubuğu'nda (sağ üst tarafta) görüntülenen ad, ana Uygulama bileşeninde tanımlanan bir durum değişkenidir. Aynı değişken hem Gezinme Çubuğu'na hem de Ana sayfaya aktarılır. Yukarıda kullanılan basit form aslında EditPage bileşenindeki "ad" durum değişkenini günceller.

Aşağıda sunulan iki yaklaşım uygulamanın ayrıntılarına girecektir:

İlk Yaklaşım: useState

useState()

useState, en sık kullanılan React kancalarından biridir; bir React Functional bileşeninde durum oluşturmanıza ve değiştirmenize olanak tanır. useState bileşeninin gerçekten basit bir uygulaması vardır ve kullanımı kolaydır: yeni bir durum oluşturmak için, durumunuzun başlangıç ​​değeriyle useState'i çağırmanız gerekir ve useState kancası iki değişken içeren bir dizi döndürecektir: ilki durumdur durumunuza referans vermek için kullanabileceğiniz bir değişken ve ikincisi, durumun değerini değiştirmek için kullandığınız bir fonksiyon: oldukça basit.

Bunu eylem halinde görmeye ne dersiniz? Gezinme Çubuğu'nda (sağ üst tarafta) görüntülenen ad, ana Uygulama bileşeninde tanımlanan bir durum değişkenidir. Aynı değişken hem Gezinme Çubuğu'na hem de Ana sayfaya aktarılır. Yukarıda kullanılan basit form aslında EditPage bileşenindeki "ad" durum değişkenini günceller. Özet olarak şudur: useState, bir başlangıç ​​durumunu parametre olarak kabul eden ve iki değer tutan iki değişkeni, başlangıç ​​durumunu içeren durum değişkenini ve aynı durum değişkeni için bir ayarlayıcı işlevini döndüren bir çekirdek kancadır.

Bunu parçalara ayıralım ve ilk etapta bunun nasıl uygulandığını görelim.

  1. “Ad” durum değişkenini oluşturmak:

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

Sahne Aksesuarları

Prop'lar bir reaksiyon bileşeninin temel yapı taşlarından biridir; kavramsal olarak, eğer bir React işlevsel bileşenini bir Javascript fonksiyonu olarak düşünürseniz, o zaman prop'lar fonksiyon parametrelerinden başka bir şey değildir; prop'ları ve useState kancasını birleştirmek size sağlam bir çerçeve sunabilir. Basit bir React uygulamasında durumu yönetmek için.

React prop'ları özel bileşenlere nitelik olarak aktarılır. Props olarak iletilen nitelikler, argüman olarak kabul edilirken props nesnesinden şuna benzer şekilde yapılandırılabilir:

Geçen aksesuarlar

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

Prop'lar normal fonksiyon argümanlarına benzer şekilde fonksiyonel bir bileşen içinde kabul edilebilir ve kullanılabilir. Bunun nedeni, "ad"ın Home bileşenine bir destek olarak iletilmesidir, bu nedenle onu aynı bileşende işleyebiliriz. Aşağıdaki örnekte, name özelliğini props nesnesinden çıkarmak için yıkıcı sözdizimini kullanarak iletilen prop'u kabul ediyoruz.

Sahne donanımı kabul ediliyor

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

Profesyonel İpucu

Tarayıcının konsolunu açın ve "name" özelliğini kullanan tüm bileşenlerin durum değiştiğinde nasıl yeniden oluşturulduğunu görün. Bir durum değişkenini manipüle ederken React bir sonraki durumu saklayacak, bileşeninizi yeni değerlerle yeniden oluşturacak ve kullanıcı arayüzünü güncelleyecektir.

Components Re-rendering

useState'in Dezavantajları

Sahne-Sondaj

Aksesuar sondajı, bir bileşen kümesinin bir ana bileşen tarafından sağlanan belirli desteklere ihtiyaç duyduğu bileşen hiyerarşisini ifade eden bir terimdir; deneyimsiz geliştiricilerin genellikle kullandığı ortak bir geçici çözüm, bu destekleri tüm bileşen zinciri boyunca iletmektir; bununla ilgili sorun Yaklaşım, bu desteklerden herhangi birinde yapılacak bir değişikliğin, tüm bileşen zincirinin yeniden oluşturulmasını tetikleyeceği, bu gereksiz işlemelerin bir sonucu olarak tüm uygulamayı etkili bir şekilde yavaşlatacağı ve bu destekleri gerektirmeyen zincirin ortasındaki bileşenlerin olduğu yönündedir. sahne aktarımı için ortam görevi görür.

Örnek:

  • Ana App bileşeninde useState() kancası kullanılarak bir durum değişkeni tanımlandı

  • Navbar bileşenine bir ad özelliği aktarıldı

  • Aynı pervane Navbar'da kabul edildi ve bir kez daha pervane olarak UserDropdown bileşenine aktarıldı

  • UserDropdown, pervaneyi kabul eden son alt öğedir.

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

Hepsi aynı durumu geçiren ve işleyen farklı bileşen katmanlarına sahip bir React uygulamasını sürdürmenin ne kadar zor olduğunu hayal edin.

Artan Karmaşıklık ve Kod Kalitesi

Bir tepki uygulamasında durum yönetiminin tek aracı olarak useState ve props'ın kullanılmasıyla kod tabanı hızla karmaşıklaşabilir, birbirinin kopyası olabilen, farklı dosyalara dağılmış onlarca veya yüzlerce durum değişkenini yönetmek zorunda kalabilir ve bileşenler oldukça göz korkutucu olabilir; belirli bir durum değişkeninde yapılacak herhangi bir değişiklik, zaten yavaş olan bir uygulamada herhangi bir potansiyel ek yeniden işlemeyi önlemek için bileşenler arasındaki bağımlılıkların dikkatli bir şekilde değerlendirilmesini gerektirecektir.

İkinci Yaklaşım: Bağlam API'si

Context API, React'ın durum yönetimi için yalnızca props ve useState kullanmanın dezavantajlarını çözme girişimidir; özellikle bağlam API'si, prop'ları tüm bileşen ağacından aşağı aktarma ihtiyacıyla ilgili olarak daha önce bahsedilen soruna bir yanıt olarak gelir. Bağlamın kullanılmasıyla, küresel olarak kabul ettiğiniz veriler için bir durum tanımlayabilir ve bu duruma bileşen ağacındaki herhangi bir noktadan erişebilirsiniz: artık pervane delmeye gerek yok.

Bağlam API'sinin başlangıçta, paylaşılması gereken diğer veri türleri için küresel veri paylaşımı, kullanıcı arayüzü temaları gibi veriler, kullanım durumumuz olan kimlik doğrulama bilgileri, diller ve benzeri verileri çözmek için tasarlandığını belirtmek önemlidir. birden fazla bileşen arasında yer alır ancak tüm uygulama için genel olmayabilir; bağlam en iyi seçenek olmayabilir; kullanım durumuna bağlı olarak bileşen kompozisyonu gibi diğer teknikleri düşünebilirsiniz. /composition-vs-inheritance.html) bu makalenin kapsamı dışındadır.

Bağlama Geçiş

Kimlik Doğrulama bağlamını oluşturma

createContext basittir, yeni bir bağlam değişkeni oluşturur ve isteğe bağlı tek bir parametreyi alır: bağlam değişkeninin varsayılan değeri.

Bunu uygulamalı olarak görelim, önce yeni bir "contexts" klasörü ve onun içinde "Auth.jsx" adlı yeni bir dosya oluşturun. Yeni bir bağlam oluşturmak için createContext() işlevini çağırmamız, döndürülen değeri daha sonra dışa aktarılacak olan yeni bir Auth değişkenine atamamız gerekir:

./src/contexts/Auth.jsx

import { createContext } from "react";

export const Auth = createContext();

Kimlik Doğrulama Bağlamını Sağlayın

Şimdi daha önce oluşturduğumuz "Auth" içerik değişkenini açığa çıkarmamız gerekiyor, bunu başarmak için içerik sağlayıcıyı kullanıyoruz, içerik sağlayıcı "değer" özelliğine sahip bir bileşendir, bu desteği bir değeri paylaşmak için kullanabiliriz - isim – bileşen ağacı boyunca, bileşen ağacını sağlayıcı bileşenle sararak, bu değeri, her bir alt bileşene ayrı ayrı aktarmaya gerek kalmadan, bileşen ağacının herhangi bir yerinden erişilebilir hale getiririz.

Kimlik doğrulama örneğimizde, kullanıcı nesnesini tutacak bir değişkene ve bu durum değişkenini değiştirecek bir tür ayarlayıcıya ihtiyacımız var; bu, useState için mükemmel bir kullanım durumudur. Bağlamı kullanırken, sağlamak istediğiniz verileri tanımladığınızdan ve bu verileri (örneğimizdeki kullanıcı) iç içe geçmiş tüm bileşen ağacına aktardığınızdan emin olmanız gerekir; bu nedenle, sağlayıcı bileşeninin içinde yeni bir durum değişkeni kullanıcı tanımlayın ve son olarak, sağlayıcının bileşen ağacında göstereceği değer olarak hem user hem de setUser'ı bir dizinin içine aktarıyoruz:

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

Yapabileceğimiz başka bir şey de "ad" durum değişkenimizi ana uygulama bileşeninden Kimlik Doğrulama bağlamına taşımak ve onu iç içe geçmiş bileşenlere göstermektir:

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

Artık geriye kalan tek şey, uygulamamızı az önce dışa aktardığımız AuthProvider bileşeninin içine yerleştirmektir.

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

Çocuk pervanesini Auth.Provider içinde oluşturduğumuz için, AuthProvider bileşeninin içinde yer alan tüm öğeler artık Auth.Provider'a ilettiğimiz değer pervanesini tüketebilmektedir. Kafa karıştırıcı görünebilir, ancak bir kez deneme yaptıktan sonra küresel bir - Bağlam - durumu sağlamayı ve tüketmeyi deneyin. Sonuçta, Context API'yi denedikten sonra bana mantıklı geldi.

Kimlik Doğrulama Bağlamını Kullanma

Son adım basittir, "Auth" bağlam sağlayıcısının sağladığı değere erişmek için "useContext" bağlam kancasını kullanırız, bu bizim durumumuzda user ve setUser'ı içeren dizidir, aşağıdaki kodda şunu yapabiliriz: Gezinme Çubuğu içindeki Kimlik Doğrulama içeriğini kullanmak için useContext'i kullanın. Bu yalnızca Navbar'ın App bileşeninin içine yerleştirildiği ve AuthProvider'ın App bileşeninin çevresini sardığı için prop değeri yalnızca useContext kancası kullanılarak tüketilebildiği için mümkündür. React'ın, küresel olarak erişilebilen ve aynı zamanda herhangi bir tüketici bileşeni tarafından manipüle edilebilen her türlü veriyi yönetmek için kutudan çıktığı gibi sunduğu başka bir harika araç da vardır.

./src/components/Navbar.jsx

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

 return (...)};

Navbar() işlevsel bileşeninde artık hiçbir desteği kabul etmediğimize dikkat edin. Bunun yerine Auth bağlamını tüketmek için useContext(Auth) kullanıyoruz, hem name hem de setName'i alıyoruz. Bu aynı zamanda artık desteği Navbar'a aktarmamıza gerek olmadığı anlamına da geliyor:

./src/App.jsx

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

Kimlik Doğrulama İçeriğini Güncelleme

"Ad" durum değişkenini değiştirmek için sağlanan setName işlevini de kullanabiliriz:

./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() kancasına giriş

Önceki örnekte, durumumuzu bileşen ağacında yönetmek ve paylaşmak için bağlam API'sini kullandık, mantığımızın temeli olarak hala useState kullandığımızı fark etmiş olabilirsiniz ve durumumuz hala basit bir nesne olduğu için bu çoğunlukla sorun değil. Bu noktada, ancak kimlik doğrulama akışımızın yeteneklerini genişletecek olsaydık, kesinlikle o anda oturum açmış olan kullanıcının e-postasından daha fazlasını saklamamız gerekecek ve bu, daha önce girdiğimiz sınırlamalara geri döndüğümüz yer. useState'in karmaşık durumla kullanımı, neyse ki React, karmaşık durumu yönetmek için useState'e bir alternatif sunarak bu sorunu çözer: useReducer'ı girin.

useReducer, useState'in genelleştirilmiş bir versiyonu olarak düşünülebilir; iki parametre alır: bir redüktör işlevi ve bir başlangıç ​​durumu.

Başlangıç ​​durumu için dikkat edilmesi gereken ilginç bir şey yok; sihir, redüktör fonksiyonunun içinde gerçekleşir: meydana gelen eylemin türünü kontrol eder ve bu eyleme bağlı olarak, redüktör, duruma hangi güncellemelerin uygulanacağını belirleyecek ve yeni değerini döndürecektir. .

Aşağıdaki koda bakıldığında, redüktör fonksiyonunun iki olası eylem türü vardır:

  • "LOGIN": bu durumda kullanıcı durumu, eylem yükünde sağlanan yeni kullanıcı kimlik bilgileriyle güncellenecektir.

  • "OTURUMU KAPAT": bu durumda kullanıcı durumu yerel depolamadan kaldırılacak ve tekrar null değerine ayarlanacaktır.

Eylem nesnesinin hem hangi mantığın uygulanacağını belirleyen bir tür alanı hem de bu mantığın uygulanması için gerekli verileri sağlayan isteğe bağlı bir yük alanı içerdiğini unutmamak önemlidir.

Son olarak, useReducer kancası mevcut durumu ve redüktöre bir eylem iletmek için kullandığımız bir gönderme fonksiyonunu döndürür.

Mantığın geri kalanı önceki örnekle aynıdır:

./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 ayarlayıcı işlevini kullanmak yerine eylemleri gönderme – örneğin: setName –

Az önce de belirttiğimiz gibi, redüktöre bir action iletmek için dispatch fonksiyonunu kullanıyoruz, aşağıdaki kodda LOGIN action başlatıyoruz ve payload olarak kullanıcıya email sağlıyoruz, artık user state'i güncellenecek ve bu değişiklik tetiklenecek Kullanıcı durumuna abone olan tüm bileşenlerin yeniden işlenmesi. Yeniden işlemenin yalnızca durumda gerçek bir değişiklik olması durumunda tetikleneceğini, düşürücü aynı önceki duruma geri dönerse yeniden işlemenin olmayacağını belirtmek önemlidir.

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

Kullanıcı Girişi

JWT Local storage

Profesyonel İpucu

Başarılı bir oturum açma işleminden sonra aldığımız kullanıcı nesnesinin artık localStorage'da nasıl saklandığına dikkat edin.

Giriş için özel kanca

Artık useReducer'ı iyi anladığımıza göre, oturum açma ve oturum kapatma mantığımızı, Oturum Açma kancasına yapılan tek bir çağrı ile kendi ayrı özel kancalarına daha fazla kapsülleyebiliriz, oturum açma rotasına bir API çağrısını yönetebilir, yeni kullanıcıyı alabiliriz Kimlik bilgilerini alın ve bunları yerel depoda saklayın, kullanıcı durumunu güncellemek için bir LOGIN çağrısı gönderin ve tüm bunları hata işlemeyle uğraşırken yapın:

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

Not: Okuyucular arasındaki daha ileri düzey React kullanıcıları için, kullanıcı kimlik bilgilerini almak için useReducer'ın tembel başlatma özelliğini neden kullanmadığımızı merak ediyor olabilirsiniz; useReducer, init işlevi adı verilen üçüncü bir isteğe bağlı parametreyi kabul eder; bu işlev, aşağıdaki durumlarda kullanılır: durumun başlangıç ​​değerini alabilmemiz için önce biraz mantık uygulamamız gerekiyor, bunu tercih etmememizin nedeni basit bir endişelerin ayrılması meselesidir, kodun bu şekilde anlaşılması daha basittir ve sonuç olarak bakımı daha kolaydır .

Giriş Sayfası

Oturum açma işlevini çıkarmak için useLogin() kancasını kullandıktan ve kullanıcı tarafından gönderilen kimlik bilgileriyle oturum açma işlevini çağırdıktan sonra Oturum Açma sayfamızın üst kısmının nasıl göründüğü:

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

Kullanıcı formu gönderdiğinde Gönder işlevini de devre dışı bırakıyoruz:

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

Ve arka ucumuzdan aldığımız kimlik doğrulama hatalarını işliyoruz:

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

Rendering Errors

Kullanıcı durumunun korunması

Kullanıcı nesnesini neden localStorage'da saklamamız gerektiğini merak ediyor olabilirsiniz; basitçe söylemek gerekirse, belirtecin süresi dolmadığı sürece kullanıcının oturumunu açık tutmak istiyoruz. localStorage kullanmak, tıpkı örneğimizde olduğu gibi, JSON parçalarını depolamanın harika bir yoludur. Giriş yaptıktan sonra sayfayı yenilerseniz durumun nasıl silindiğine dikkat edin. Bu, localStorage'da kayıtlı bir kullanıcı nesnesinin olup olmadığını kontrol etmek için bir useEffect kancası kullanılarak kolayca çözülebilir, eğer varsa, kullanıcıya otomatik olarak giriş yaparız:

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

Oturumu Kapatmak için özel kancalar

Aynı şey Oturum Kapatma kancası için de geçerlidir, burada mevcut kullanıcı kimlik bilgilerini hem durumdan hem de yerel depolamadan kaldırmak için bir OTURUMU ÇIKARMA eylemi gönderiyoruz:

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

Kullanıcı Oturumu Kapatma

Bir kullanıcının oturumunu kapatmak için UserDropdown.jsx'te bulunan Oturumu Kapat düğmesine bir tıklama olayı ekleyelim ve buna göre işlem yapalım:

./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 Route'ları Korumak

Uygulamamızı uygulamanın son adımı, kullanıcı gezinmesini kontrol etmek için küresel kullanıcı durumundan yararlanmaktır; bu, hangi davranışı elde etmemiz gerektiğine dair kısa bir hatırlatmadır: başlangıçta kullanıcı oturum açma sayfası tarafından karşılanır, bu noktadan sonra kullanıcı yalnızca ana sayfaya erişebilir. Başarılı bir girişin ardından, benzer şekilde kullanıcı, çıkış yaptığında giriş sayfasına yönlendirilecektir.

Bunu, react-router-dom kütüphanesinin yardımıyla 2 rota tanımlayarak başarıyoruz: "/" ve "/login", global kimlik doğrulama durumunu kullanarak her rotada hangi bileşenin oluşturulacağını kontrol ediyoruz kimlik doğrulamasının null olarak değerlendirilmesi, kimliği doğrulanmamış bir kullanıcıyı temsil eder ve bunun tersi de geçerlidir:

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

Özet Diyagramı

Diagram

Özet

Bu makalede, bir kimlik doğrulama iş akışı için durum yönetiminin uygulanmasına ilişkin basit ama çok yaygın kullanım durumunu, farklı yaklaşımları, her birinin ardındaki mantığı ve bunların ödünlerini ele almaya çalıştık. İstemci tarafı çerçevelerinde ve özellikle React'ta durum yönetimi, ön uç topluluğunda en çok konuşulan konulardan biridir, çünkü uygulamanızın performansını ve ölçeklenebilirliğini artırabilir veya bozabilir. Bu devlet yönetimi sorununu çözmeye çalışan farklı tekniklerin, kalıpların, kitaplıkların ve araçların çokluğu karşı konulmazdır; amacımız size uygulamalar hakkında sağlam bir anlayış kazandırmaktı, böylece bunu daha ileri düzeyde kendi uygulamanızda uygulayabilirsiniz. Daha karmaşık uygulamalarda durum yönetimi teknikleri ve kalıpları hakkında bilgi edinmek için, React'te ölçeklenebilir durum yönetimi için redux'a gireceğimiz bir sonraki makalemize göz atın.

Yakında gelecek

ContextAPI ve Redux Araç Takımı karşılaştırması

Redux, Redux araç setinden önce toplulukta uzun yıllar benimsenmişti; aslında RTK, yeni uygulamalarda redux durumu yönetimini önyüklemek için bir başlangıç ​​şablonu olarak tanıtıldı (başlangıç ​​adı Ekim 2019'da "redux-starter-kit" idi) Bugün, Redux bakımcıları ve topluluk arasında, Redux araç setinin redux ile çalışmanın geçerli yolu olduğu konusunda genel bir fikir birliği var. RTX, Redux mantığının çoğunu soyutlayarak, çok daha az ayrıntıyla kullanımını kolaylaştırıyor ve geliştiricileri, Redux'u kullanmaya teşvik ediyor. En iyi uygulamaları takip edin, ikisi arasındaki temel fark, Redux'un görüşlerden uzak olacak şekilde tasarlanmış olması, minimal bir API sağlaması ve geliştiricilerin ortak görevler için kendi kitaplıklarını yazarak ve kod yapısıyla ilgilenerek ağır işlerin çoğunu yapmalarını beklemesidir. bu, geliştirme süresinin yavaşlamasına ve kodun karmaşık olmasına neden oldu. Redux araç seti, geliştiricilerin ortak tuzaklara düşmesini engelleyen ekstra bir soyutlama katmanı olarak eklendi. Daha fazla bilgi ve bakımcılarının gerekçeleri için resmi belgelere bakın burada.


Career Services background pattern

Kariyer Hizmetleri

Contact Section background image

İletişimde kalalım

Code Labs Academy © 2025 Her hakkı saklıdır.