Manajemen Negara di React: Contoh Praktis

reaksi
manajemen negara
api konteks
Manajemen Negara di React: Contoh Praktis cover image

React adalah kerangka kerja untuk membangun aplikasi sisi klien yang dinamis bagi banyak pengembang. Sifat dinamis dari aplikasi ini berasal dari fleksibilitas dan daftar kemampuan dan fitur yang diperluas yang mungkin ada di sisi klien yang memungkinkan pengembang untuk membangun aplikasi lengkap yang dimuat di browser dalam hitungan detik, suatu prestasi yang tidak pernah dicapai sebelumnya. mungkin (atau sangat rumit) di zaman web statis.

Dengan perluasan kemungkinan ini, muncullah konsep pengelolaan negara, seiring dengan meningkatnya kompleksitas dalam aplikasi sisi klien, kebutuhan untuk menjaga keadaan lokal menjadi hambatan tersendiri jika tidak ditangani dengan benar dan dipahami dengan mempertimbangkan skalabilitas.

Permasalahan ini diatasi dengan banyak kerangka kerja, mengikuti pendekatan yang berbeda-beda dan fokus pada rangkaian submasalah yang berbeda-beda, oleh karena itu penting untuk memiliki pemahaman tingkat tinggi tentang ekosistem kerangka kerja pilihan untuk menilai kebutuhan setiap aplikasi dan menggunakan pendekatan yang tepat setelahnya. metrik. Artikel ini akan memberi Anda gambaran singkat tentang masalah umum pengelolaan status dan mencoba memperkenalkan pendekatan berbeda (useState, Context API) sebagai respons terhadapnya. Meskipun artikel ini akan menyajikan berbagai solusi, artikel ini hanya akan berfokus pada tantangan dalam skala yang lebih kecil. Kami akan membahas topik yang lebih maju di artikel mendatang.

Alur Kerja Otentikasi

Kode yang ditampilkan di seluruh artikel dapat ditemukan di sini.

Link pratinjau langsung dapat diakses di sini.

Pertimbangkan kasus di mana kita menerapkan proses otentikasi untuk aplikasi React.

User Login

Seperti yang ditunjukkan pada GIF di atas, kami ingin mengizinkan pengguna untuk masuk atau mendaftar ke aplikasi kami menggunakan kredensial. Jika kredensial yang valid diberikan, pengguna akan login, aplikasi akan secara otomatis menavigasi ke halaman beranda dan pengguna dapat melanjutkan untuk menggunakan aplikasi.

Demikian pula, Jika pengguna logout, sumber daya halaman beranda akan dilindungi di balik login, halaman login akan menjadi satu-satunya halaman yang dapat diakses oleh pengguna.

Memikirkan alur kerja ini dalam hal implementasi, kita akan memiliki komponen utama bernama App, komponen App akan mengarahkan pengguna ke salah satu dari dua halaman: Beranda atau Login, status pengguna saat ini (login, logout) akan menentukan yang mana halaman tempat pengguna diarahkan, perubahan pada status pengguna saat ini (misalnya perubahan dari login ke logout) akan memicu pengalihan instan ke halaman terkait.

State

Seperti yang ditunjukkan dalam ilustrasi di atas, kami ingin komponen Aplikasi mempertimbangkan status saat ini dan hanya merender salah satu dari dua halaman – Beranda atau Login – berdasarkan status saat ini.

Jika penggunanya null, berarti kami tidak memiliki pengguna yang diautentikasi, jadi kami menavigasi ke halaman login secara otomatis dan melindungi halaman beranda menggunakan rendering bersyarat. Jika pengguna ada, kami melakukan yang sebaliknya.

Sekarang kita memiliki pemahaman yang kuat tentang apa yang harus diimplementasikan, mari kita jelajahi beberapa opsi, tapi pertama-tama mari kita siapkan proyek React kita,

Repo proyek berisi contoh aplikasi backend yang akan kita gunakan untuk mengimplementasikan sisi frontend (kita tidak akan membahasnya karena ini bukan fokus utama di sini tetapi kodenya sengaja dibuat sederhana sehingga Anda tidak akan kesulitan melakukannya )

Kita mulai dengan membuat halaman dan komponen berikut:

  • Halaman Beranda

  • Halaman Masuk

  • Edit Halaman Pengguna

  • Komponen Navbar

  • Komponen Dropdown Pengguna

Aplikasi React dengan banyak halaman memerlukan navigasi yang tepat, untuk itu kita dapat menggunakan react-router-dom untuk membuat konteks router browser global dan mendaftarkan rute React yang berbeda.


yarn add react-router-dom

Kami tahu banyak pembaca lebih suka mengikuti tutorial, jadi inilah template awal untuk membantu Anda mengetahui informasi terbaru. Cabang pemula ini menggunakan DaisyUI untuk komponen TailwindCSS JSX yang telah ditentukan sebelumnya. Ini mencakup semua komponen, halaman, dan router yang sudah dikonfigurasi. Jika Anda mempertimbangkan untuk mengikuti tutorial ini, membangun seluruh alur autentikasi sendiri dengan mengikuti langkah-langkah mudah, mulailah dengan mem-fork repositori terlebih dahulu. Setelah Anda melakukan fork pada repositori, kloning dan mulai dari start-herebranch:


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

Setelah Anda menarik cabang start-here:

  • Buka proyek dengan editor kode pilihan Anda

  • Ubah direktori menjadi frontend/

  • Instal dependensi: benang

  • Mulai server pengembangan: pengembang benang·

Pratinjaunya akan terlihat seperti ini:

Changing user name

Nama yang ditampilkan di Navbar – sisi kanan atas – adalah variabel status yang ditentukan dalam komponen Aplikasi utama. Variabel yang sama diteruskan ke Navbar dan halaman Beranda. Bentuk sederhana yang digunakan di atas sebenarnya memperbarui variabel status “nama” dari komponen EditPage.

Kedua pendekatan yang disajikan di bawah ini akan menjelaskan secara rinci implementasinya:

Pendekatan Pertama: useState

keadaan penggunaan()

useState adalah salah satu React hooks yang paling umum digunakan, ini memungkinkan Anda membuat dan mengubah status dalam komponen React Functional. Komponen useState memiliki implementasi yang sangat sederhana dan mudah digunakan: untuk membuat keadaan baru, Anda perlu memanggil useState dengan nilai awal dari keadaan Anda dan hook useState akan mengembalikan sebuah array yang berisi dua variabel: yang pertama adalah keadaan variabel yang dapat Anda gunakan untuk mereferensikan status Anda, dan variabel kedua adalah fungsi yang Anda gunakan untuk mengubah nilai status: cukup mudah.

Bagaimana kalau kita melihatnya dalam tindakan? Nama yang ditampilkan di Navbar – sisi kanan atas – adalah variabel status yang ditentukan dalam komponen Aplikasi utama. Variabel yang sama diteruskan ke Navbar dan halaman Beranda. Bentuk sederhana yang digunakan di atas sebenarnya memperbarui variabel status “nama” dari komponen EditPage. Intinya begini: useState adalah hook inti yang menerima keadaan awal sebagai parameter dan mengembalikan dua variabel yang memiliki dua nilai, variabel keadaan yang berisi keadaan awal, dan fungsi penyetel untuk variabel keadaan yang sama.

Mari kita uraikan, dan lihat bagaimana hal ini diterapkan.

  1. Membuat variabel status “nama”:

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

Alat peraga

Props adalah salah satu blok penyusun dasar dari komponen reaksi, secara konseptual, jika Anda menganggap komponen fungsional React sebagai fungsi Javascript, maka props tidak lebih dari parameter fungsi, menggabungkan props dan useState hook dapat memberi Anda kerangka kerja yang solid untuk mengelola status di seluruh aplikasi React sederhana.

Props React diteruskan sebagai atribut ke komponen khusus. Atribut yang diteruskan sebagai props dapat didestrukturisasi dari objek props ketika menerimanya sebagai argumen, serupa dengan ini:

Melewati alat peraga

<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 dapat diterima dan digunakan di dalam komponen fungsional yang mirip dengan argumen fungsi normal. Itu karena "nama" diteruskan sebagai prop ke komponen Home, sehingga kita dapat merendernya di komponen yang sama. Pada contoh berikut, kita menerima prop yang diteruskan menggunakan sintaks destrukturisasi untuk mengekstrak properti nama dari objek props.

Menerima alat peraga

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

Tip Profesional

Buka konsol browser, dan perhatikan bagaimana semua komponen yang menggunakan prop "nama" dirender ulang ketika statusnya berubah. Saat memanipulasi variabel status, React akan menyimpan status berikutnya, merender komponen Anda lagi dengan nilai baru, dan memperbarui UI.

Components Re-rendering

Kelemahan useState

Alat Peraga-Pengeboran

Pengeboran alat peraga adalah istilah untuk merujuk pada hierarki komponen di mana sekumpulan komponen memerlukan alat peraga tertentu yang disediakan oleh komponen induk, solusi umum yang biasanya digunakan oleh pengembang yang tidak berpengalaman adalah dengan meneruskan alat peraga ini ke seluruh rantai komponen, masalahnya dengan ini Pendekatannya adalah bahwa perubahan pada salah satu props ini akan memicu seluruh rantai komponen untuk dirender ulang, yang secara efektif memperlambat seluruh aplikasi sebagai akibat dari render yang tidak perlu ini, komponen di tengah rantai yang tidak memerlukan props ini bertindak sebagai media untuk mentransfer alat peraga.

Contoh:

  • Variabel status didefinisikan dalam komponen Aplikasi utama menggunakan kait useState()

  • Prop nama diteruskan ke komponen Navbar

  • Prop yang sama diterima di Navbar dan diteruskan sebagai prop sekali lagi ke komponen UserDropdown

  • UserDropdown adalah elemen anak terakhir yang menerima prop.

Prop-drilling

./src/App.jsx

... ...
function App() {
 const [name, setName] = useState(test);
 console.log("Rendering: App");

 return (
   <BrowserRouter>
     <div className="h-screen">
       <Navbar name={name} />

       <main className="px-4">
... ...

./src/components/Navbar.jsx

import React from "react";
import Logo from "../assets/cla.svg";
import { BiSearchAlt } from "react-icons/bi";
import { Link } from "react-router-dom";
import UserDropdown from "./UserDropdown";

export default function Navbar({ name }) {
  console.log("Rendering: Navbar");

  return (
    <>
      <div className="navbar bg-base-100 drop-shadow-sm">
        <div className="flex-1">
          <Link
            to="/"
            className="btn btn-ghost normal-case text-md md:text-xl px-2 gap-1"
          >
            <img src={Logo} className="h-6" alt="" />
            Resources
          </Link>
        </div>

        <UserDropdown name={name} />
      </div>
    </>
  );
}

Bayangkan betapa sulitnya mempertahankan aplikasi React dengan lapisan komponen berbeda yang meneruskan dan merender status yang sama.

Meningkatnya Kompleksitas dan Kualitas Kode

Dengan penggunaan useState dan props sebagai satu-satunya sarana pengelolaan status dalam aplikasi reaksi, basis kode dapat dengan cepat bertambah kompleks, karena harus mengelola puluhan atau ratusan variabel status, yang dapat berupa duplikat satu sama lain, tersebar di berbagai file dan komponen bisa jadi sangat menakutkan, setiap perubahan pada variabel status tertentu akan memerlukan pertimbangan yang cermat terhadap ketergantungan antar komponen untuk menghindari potensi rendering ulang tambahan pada aplikasi yang sudah lambat.

Pendekatan Kedua: API Konteks

Context API adalah upaya React untuk memecahkan kelemahan dari hanya menggunakan props dan useState untuk manajemen negara, khususnya, API konteks hadir sebagai jawaban atas masalah yang disebutkan sebelumnya mengenai kebutuhan untuk meneruskan props ke seluruh pohon komponen. Dengan menggunakan konteks, Anda dapat menentukan status untuk data yang Anda anggap global dan mengakses statusnya dari titik mana pun di pohon komponen: tidak perlu lagi melakukan pengeboran penyangga.

Penting untuk diketahui bahwa API konteks pada awalnya disusun untuk menyelesaikan masalah berbagi data global, data seperti tema UI, informasi autentikasi yang merupakan kasus penggunaan kami, bahasa, dan sejenisnya), untuk jenis data lain yang perlu dibagikan di antara lebih dari satu komponen tetapi belum tentu bersifat global untuk semua aplikasi, konteks mungkin bukan pilihan terbaik, bergantung pada kasus penggunaan, Anda dapat mempertimbangkan teknik lain seperti komposisi komponen yang berada di luar cakupan artikel ini.

Beralih ke Konteks

Membuat konteks Auth

createContext sangatlah mudah, ia membuat variabel konteks baru, dibutuhkan satu parameter opsional: nilai default dari variabel konteks.

Mari kita lihat cara kerjanya, pertama buat folder baru "konteks" dan file baru di dalamnya "Auth.jsx". Untuk membuat konteks baru, kita perlu memanggil fungsi createContext(), menetapkan nilai yang dikembalikan ke variabel baru Auth yang selanjutnya akan diekspor:

./src/contexts/Auth.jsx

import { createContext } from "react";

export const Auth = createContext();

Berikan Konteks Auth

Sekarang kita perlu mengekspos variabel konteks "Auth" yang kita buat sebelumnya, untuk mencapai hal ini kita menggunakan penyedia konteks, penyedia konteks adalah komponen yang memiliki prop "nilai", kita dapat menggunakan prop ini untuk berbagi nilai – nama – di seluruh pohon komponen, dengan menggabungkan pohon komponen dengan komponen penyedia, kita membuat nilai tersebut dapat diakses dari mana saja di dalam pohon komponen, tanpa perlu meneruskan prop tersebut ke setiap komponen turunan satu per satu.

Dalam contoh autentikasi, kita memerlukan variabel untuk menampung objek pengguna, dan semacam penyetel untuk memanipulasi variabel status tersebut, ini adalah kasus penggunaan yang sempurna untuk useState. Saat menggunakan Konteks, Anda perlu memastikan bahwa Anda mendefinisikan data yang ingin Anda berikan, dan meneruskan data tersebut – pengguna dalam contoh kita – ke semua pohon komponen yang ada di dalamnya, jadi tentukan variabel status baru pengguna di dalam komponen penyedia dan akhirnya kita memasukkan user dan setUser ke dalam array sebagai nilai yang akan diekspos oleh penyedia ke pohon komponen:

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

Hal lain yang dapat kita lakukan adalah memindahkan variabel status “nama” dari komponen aplikasi utama ke konteks Auth dan mengeksposnya ke komponen bertingkat:

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

Sekarang yang tersisa hanyalah menyarangkan aplikasi kita ke dalam komponen AuthProvider yang sama yang baru saja kita ekspor.

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

Karena kita merender prop anak-anak di dalam Auth.Provider, semua elemen yang bersarang di dalam komponen AuthProvider sekarang dapat menggunakan prop nilai yang kita teruskan ke Auth.Provider. Ini mungkin tampak membingungkan, tetapi setelah Anda bereksperimen dengannya, cobalah menyediakan dan menggunakan status - Konteks - global. Lagi pula, ini baru masuk akal bagi saya setelah bereksperimen dengan API Konteks.

Menggunakan Konteks Auth

Langkah terakhir sangat mudah, kita menggunakan kait konteks "useContext" untuk mengakses nilai yang disediakan oleh penyedia konteks "Auth", yang dalam kasus kita adalah array yang berisi pengguna dan setUser, dalam kode berikut, kita dapat melakukannya gunakan useContext untuk menggunakan konteks Auth di dalam Navbar. Hal ini hanya mungkin karena Navbar bersarang di dalam komponen Aplikasi, dan karena AuthProvider membungkus komponen Aplikasi, prop nilai dapat digunakan hanya menggunakan kait useContext. Alat luar biasa lainnya yang disediakan React untuk mengelola data apa pun yang dapat diakses secara global dan juga dimanipulasi oleh komponen konsumen mana pun.

./src/components/Navbar.jsx

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

 return (...)};

Perhatikan bagaimana kami tidak lagi menerima props apa pun di komponen fungsional Navbar(). Kami menggunakan useContext(Auth) untuk menggunakan konteks Auth, mengambil nama dan setName. Ini juga berarti kita tidak perlu meneruskan prop ke Navbar lagi:

./src/App.jsx

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

Memperbarui Konteks Auth

Kita juga dapat menggunakan fungsi setName yang disediakan untuk memanipulasi variabel status “nama”:

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

Memperkenalkan kait useReducer()

Pada contoh sebelumnya kita menggunakan API konteks untuk mengelola dan membagikan status kita ke bawah pohon komponen, Anda mungkin memperhatikan bahwa kita masih menggunakan useState sebagai basis logika kita, dan ini sebagian besar tidak masalah karena status kita masih berupa objek sederhana pada titik ini tetapi jika kita ingin memperluas kemampuan alur autentikasi, kita pasti perlu menyimpan lebih dari sekedar email pengguna yang sedang login, dan di sinilah kita kembali ke batasan yang sebelumnya kita bahas terkait dengan penggunaan useState dengan keadaan kompleks, untungnya, React menyelesaikan masalah ini dengan memberikan alternatif selain useState untuk mengelola keadaan kompleks: masukkan useReducer.

useReducer dapat dianggap sebagai versi umum dari useState, dibutuhkan dua parameter: fungsi peredam dan keadaan awal.

Tidak ada hal menarik yang perlu diperhatikan untuk keadaan awal, keajaiban terjadi di dalam fungsi peredam: ia memeriksa jenis tindakan yang terjadi, dan bergantung pada tindakan itu, peredam akan menentukan pembaruan apa yang akan diterapkan pada keadaan dan mengembalikan nilai barunya. .

Melihat kode di bawah, fungsi peredam memiliki dua jenis tindakan yang mungkin:

  • "LOGIN": dalam hal ini status pengguna akan diperbarui dengan kredensial pengguna baru yang disediakan di dalam payload tindakan.

  • "LOGOUT": dalam hal ini status pengguna akan dihapus dari penyimpanan lokal dan disetel kembali ke null.

Penting untuk dicatat bahwa objek tindakan berisi bidang tipe yang menentukan logika apa yang akan diterapkan dan bidang payload opsional untuk menyediakan data yang diperlukan untuk menerapkan logika tersebut.

Terakhir, hook useReducer mengembalikan status saat ini dan fungsi pengiriman yang kita gunakan untuk meneruskan tindakan ke peredam.

Logika selanjutnya sama dengan contoh sebelumnya:

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

Mengirim tindakan alih-alih menggunakan fungsi penyetel setState – misalnya: setName –

Seperti yang baru saja kami sebutkan, kami menggunakan fungsi pengiriman untuk meneruskan tindakan ke peredam, dalam kode berikut, kami memulai tindakan LOGIN dan kami menyediakan email pengguna sebagai payload, sekarang status pengguna akan diperbarui dan perubahan ini akan dipicu rendering ulang semua komponen yang berlangganan status pengguna. Penting untuk diketahui bahwa perenderan ulang hanya akan terpicu jika terjadi perubahan nyata pada keadaan, tidak ada perenderan ulang jika peredam mengembalikan keadaan sebelumnya yang sama.

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

Masuk Pengguna

JWT Local storage

Tip Profesional

Perhatikan bagaimana objek pengguna yang kita terima setelah login berhasil sekarang disimpan di Penyimpanan lokal.

Kait khusus untuk Login

Sekarang kita sudah menguasai useReducer, kita dapat merangkum logika login dan logout kita ke dalam hook kustom masing-masing yang terpisah, dengan satu panggilan ke hook Login, kita dapat menangani panggilan API ke rute login, mengambil pengguna baru kredensial dan menyimpannya di penyimpanan lokal, kirimkan panggilan LOGIN untuk memperbarui status pengguna, sambil menangani penanganan kesalahan:

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

Catatan: untuk pengguna React yang lebih mahir di kalangan pembaca, Anda mungkin bertanya-tanya mengapa kami tidak menggunakan fitur inisialisasi malas useReducer untuk mengambil kredensial pengguna, useReducer menerima parameter opsional ketiga yang disebut fungsi init, fungsi ini digunakan dalam kasus kita perlu menerapkan beberapa logika sebelum kita bisa mendapatkan nilai awal keadaan, alasan kita tidak memilih ini adalah masalah pemisahan masalah yang sederhana, kode dengan cara ini lebih mudah untuk dipahami dan sebagai hasilnya lebih mudah untuk dipelihara .

Halaman Masuk

Berikut tampilan bagian atas halaman Login kami setelah menggunakan hook useLogin() untuk mengekstrak fungsi login, dan menjalankan fungsi login dengan kredensial yang dikirimkan oleh pengguna:

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

Kami juga menonaktifkan fungsi Kirim ketika pengguna mengirimkan formulir:

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

Dan menampilkan kesalahan autentikasi apa pun yang kami terima dari backend kami:

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

Rendering Errors

Mempertahankan status pengguna

Anda mungkin bertanya-tanya mengapa kami perlu menyimpan objek pengguna di Penyimpanan lokal, sederhananya, kami ingin pengguna tetap masuk selama tokennya belum kedaluwarsa. Menggunakan localStorage adalah cara terbaik untuk menyimpan bit JSON, seperti pada contoh kita. Perhatikan bagaimana keadaan terhapus jika Anda me-refresh halaman setelah login. Hal ini dapat diselesaikan dengan mudah menggunakan hook useEffect untuk memeriksa apakah kita memiliki objek pengguna yang tersimpan di localStorage, jika ada, kita login pengguna secara otomatis:

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

Kait khusus untuk Logout

Hal yang sama berlaku dengan kait Logout, di sini kami mengirimkan tindakan LOGOUT untuk menghapus kredensial pengguna saat ini dari negara bagian dan penyimpanan lokal:

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

Pengguna Keluar

Untuk logout pengguna, mari tambahkan event click ke tombol Logout yang ada di UserDropdown.jsx, dan tangani sesuai dengan itu:

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

Melindungi Rute React

Langkah terakhir dalam implementasi aplikasi kita adalah memanfaatkan status pengguna global untuk mengontrol navigasi pengguna, pengingat singkat tentang perilaku apa yang harus kita capai: awalnya pengguna disambut oleh halaman login, sejak saat itu pengguna hanya dapat mengakses halaman beranda setelah login berhasil, pengguna juga akan diarahkan ke halaman login setelah logout.

Kami mencapai hal ini dengan bantuan perpustakaan react-router-dom dengan mendefinisikan 2 rute: "/" dan "/login", kami mengontrol komponen mana yang akan dirender pada setiap rute menggunakan status autentikasi global, auth yang mengevaluasi ke null mewakili pengguna yang tidak diautentikasi dan sebaliknya:

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

Rekap Diagram

Diagram

Penutupan

Dalam artikel ini kami mencoba menangani kasus penggunaan yang sederhana namun sangat umum dalam penerapan manajemen negara untuk alur kerja autentikasi, melalui berbagai pendekatan, alasan di balik masing-masing pendekatan, dan konsekuensinya. Manajemen status dalam kerangka sisi klien dan khususnya React adalah salah satu topik yang paling banyak dibicarakan di komunitas frontend, karena hal ini dapat meningkatkan atau menghancurkan kinerja dan skalabilitas aplikasi Anda. Banyaknya teknik, pola, perpustakaan, dan alat yang berbeda yang mencoba memecahkan masalah pengelolaan negara ini sangatlah banyak, tujuan kami adalah memberi Anda pemahaman yang kuat tentang praktik tersebut sehingga Anda dapat menerapkannya dalam aplikasi Anda sendiri, untuk tingkat lebih lanjut. teknik dan pola manajemen negara dalam aplikasi yang lebih kompleks, lihat artikel kami berikutnya di mana kita masuk ke redux untuk manajemen negara yang dapat diskalakan di React.

Segera hadir

ContextAPI vs Perangkat Redux

Redux telah diadopsi selama bertahun-tahun di komunitas sebelum toolkit Redux. Faktanya, RTK diperkenalkan sebagai template awal untuk mem-bootstrap manajemen status redux dalam aplikasi baru (nama awalnya adalah “redux-starter-kit” pada bulan Oktober 2019), meskipun saat ini, ada konsensus umum antara pengelola Redux dan komunitas bahwa toolkit Redux adalah cara yang valid untuk bekerja dengan redux. RTX mengabstraksi banyak logika Redux yang membuatnya lebih mudah digunakan, dengan lebih sedikit kata-kata, dan mendorong pengembang untuk mengikuti praktik terbaik, satu perbedaan utama antara keduanya adalah bahwa Redux dibuat tanpa pendapat, menyediakan API minimal dan mengharapkan pengembang melakukan sebagian besar pekerjaan berat dengan menulis perpustakaan mereka sendiri untuk tugas-tugas umum dan menangani struktur kode, hal ini mengakibatkan waktu pengembangan lambat dan kode berantakan, toolkit Redux ditambahkan sebagai lapisan abstraksi tambahan yang mencegah pengembang jatuh ke dalam perangkap umum, lihat dokumentasi resmi untuk wawasan lebih lanjut dan alasan dari pengelolanya di sini.


Career Services background pattern

Layanan Karir

Contact Section background image

Mari tetap berhubungan

Code Labs Academy © 2024 Semua hak dilindungi undang-undang.