Membuat Halaman Login React: Dari Formulir sampai Halaman yang Dilindungi
Halaman login adalah salah satu bagian yang paling umum di situs web mana pun. Artikel ini membuatnya di React langkah demi langkah: struktur formulir, input terkendali, validasi, masuk tiruan, pengunci setelah beberapa kali gagal, status pengguna bersama dengan Context, dan halaman yang dilindungi yang menyembunyikan konten dari orang yang belum masuk. Setiap contoh sudah dijalankan, dan keluaran yang ditampilkan adalah keluaran sebenarnya.
Yang Anda butuhkan
Anda memerlukan Node.js 18 atau yang lebih baru dan pengetahuan dasar React. Contohnya memakai React 18.3.1 dan dijalankan dengan penguji artikel ini, yang mengklik tombol di browser tiruan dengan jsdom. Penting: memeriksa kata sandi di browser hanya untuk belajar. Sistem sungguhan harus memeriksa kata sandi di server, menyimpannya sebagai hash, dan memakai HTTPS.
Ilustrasi
Contoh 1: Struktur formulir login
Mulailah dengan formulir yang memiliki kolom email, kolom kata sandi, dan tombol kirim. renderToStaticMarkup menampilkan HTML yang dihasilkan React.
import React from "react";
export function App() {
return (
<form>
<h2>Masuk</h2>
<label>Email<input type="email" name="email" /></label>
<label>Kata sandi<input type="password" name="password" /></label>
<button type="submit">Masuk</button>
</form>
);
}
Keluaran sebenarnya
<form><h2>Masuk</h2><label>Email<input type="email" name="email"/></label><label>Kata sandi<input type="password" name="password"/></label><button type="submit">Masuk</button></form>
Contoh 2: Tampilkan atau sembunyikan kata sandi
Simpan status tampilan di state dan ubah tipe input antara password dan text saat tombol diklik.
1. Klik tombol tampilkan kata sandi sekali, dari nonaktif ke aktif
import React, { useState } from "react";
export function App() {
const [show, setShow] = useState(false);
return (
<div>
<input type={show ? "text" : "password"} value="secret1" readOnly />
<button id="toggle" onClick={() => setShow(!show)}>Tampilkan kata sandi</button>
<p data-out>Tampilkan kata sandi: {show ? "Aktif" : "Nonaktif"}</p>
</div>
);
}
export const steps = ["#toggle"];
Keluaran sebenarnya
Teks awal: Tampilkan kata sandi: Nonaktif
Setelah klik ke-1: Tampilkan kata sandi: Aktif
Contoh 3: Validasi input
Fungsi validate mengembalikan daftar pesan kesalahan. Tampilkan pesan itu hanya setelah pengguna mengirim, supaya formulir tidak protes selagi pengguna masih mengetik.
import React, { useState } from "react";
// Aturan validasi: mengembalikan daftar pesan peringatan. Jika tidak ada kesalahan, hasilnya larik kosong
export function validate(email, password) {
const errors = [];
if (!email) errors.push("Isi email");
else if (!/^\S+@\S+\.\S+$/.test(email)) errors.push("Format email tidak valid");
if (password.length < 6) errors.push("Kata sandi minimal 6 karakter");
return errors;
}
export function App() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [submitted, setSubmitted] = useState(false);
const errors = validate(email, password);
return (
<div>
<input value={email} onChange={e => setEmail(e.target.value)} />
<input value={password} onChange={e => setPassword(e.target.value)} />
<button id="submit" onClick={() => setSubmitted(true)}>Kirim formulir</button>
<p data-out>{!submitted ? "Formulir belum dikirim" : errors.length ? errors.join(" | ") : "Tidak ada kesalahan"}</p>
</div>
);
}
export const steps = ["#submit"];
Keluaran sebenarnya
Teks awal: Formulir belum dikirim
Setelah klik ke-1: Isi email | Kata sandi minimal 6 karakter
Contoh 4: Masuk tiruan
Bandingkan email dan kata sandi dengan data contoh, lalu tampilkan pesan berhasil atau akun tidak ditemukan. Contoh ini dimulai dengan nilai yang benar.
import React, { useState } from "react";
// Contoh database pengguna tiruan. Di sistem sungguhan, pemeriksaan hanya boleh dilakukan di server
const USERS = { "[email protected]": "secret1" };
export function login(email, password) {
if (USERS[email] === undefined) return { ok: false, message: "Akun ini tidak ditemukan" };
if (USERS[email] !== password) return { ok: false, message: "Kata sandi tidak valid" };
return { ok: true, message: "Berhasil masuk: " + email };
}
export function App() {
const [email, setEmail] = useState("[email protected]");
const [password, setPassword] = useState("secret1");
const [result, setResult] = useState("Belum mencoba masuk");
return (
<div>
<input value={email} onChange={e => setEmail(e.target.value)} />
<input value={password} onChange={e => setPassword(e.target.value)} />
<button id="submit" onClick={() => setResult(login(email, password).message)}>Masuk</button>
<p data-out>{result}</p>
</div>
);
}
export const steps = ["#submit"];
Keluaran sebenarnya
Teks awal: Belum mencoba masuk
Setelah klik ke-1: Berhasil masuk: [email protected]
Contoh 5: Kunci setelah gagal berulang kali
Hitung kata sandi yang salah, dan nonaktifkan tombol ketika batasnya tercapai, untuk memperlambat tebakan yang berulang.
import React, { useState } from "react";
const MAX_TRIES = 3;
export function App() {
const [password, setPassword] = useState("wrong");
const [tries, setTries] = useState(0);
const [message, setMessage] = useState("");
const locked = tries >= MAX_TRIES;
function submit() {
if (locked) return;
if (password === "secret1") {
setMessage("Berhasil masuk");
return;
}
const next = tries + 1;
setTries(next);
setMessage(next >= MAX_TRIES ? "Dikunci sementara, coba lagi nanti" : `Kata sandi tidak valid (percobaan ke-${next} dari ${MAX_TRIES})`);
}
return (
<div>
<input value={password} onChange={e => setPassword(e.target.value)} />
<button id="submit" disabled={locked} onClick={submit}>Masuk</button>
<p data-out>{message || "Siap digunakan"}</p>
</div>
);
}
export const steps = ["#submit", "#submit", "#submit"];
Keluaran sebenarnya
Teks awal: Siap digunakan
Setelah klik ke-1: Kata sandi tidak valid (percobaan ke-1 dari 3)
Setelah klik ke-2: Kata sandi tidak valid (percobaan ke-2 dari 3)
Setelah klik ke-3: Dikunci sementara, coba lagi nanti
Contoh 6: Opsi ingat saya
Kotak centang yang dikendalikan oleh state, dengan status yang langsung ditampilkan kepada pengguna.
import React, { useState } from "react";
export function App() {
const [remember, setRemember] = useState(false);
return (
<div>
<label>
<input id="remember" type="checkbox" checked={remember} onChange={e => setRemember(e.target.checked)} />
Ingat saya
</label>
<p data-out>Ingat saya: {remember ? "Aktif" : "Nonaktif"}</p>
</div>
);
}
export const steps = ["#remember"];
Keluaran sebenarnya
Teks awal: Ingat saya: Nonaktif
Setelah klik ke-1: Ingat saya: Aktif
Contoh 7: Simpan status pengguna dengan Context
Buat AuthContext agar bilah status dan tombol-tombolnya dapat mengakses data pengguna tanpa meneruskan props melalui beberapa tingkat.
import React, { createContext, useContext, useState } from "react";
// Simpan status pengguna di Context agar setiap bagian halaman dapat mengaksesnya tanpa meneruskan props berlapis-lapis
const AuthContext = createContext(null);
function StatusBar() {
const { user, login, logout } = useContext(AuthContext);
return (
<div>
<p data-out>Status: {user ? "Halo " + user : "Belum masuk"}</p>
{user
? <button id="logout" onClick={logout}>Keluar</button>
: <button id="login" onClick={() => login("Budi")}>Masuk</button>}
</div>
);
}
export function App() {
const [user, setUser] = useState(null);
const value = { user, login: setUser, logout: () => setUser(null) };
return (
<AuthContext.Provider value={value}>
<StatusBar />
</AuthContext.Provider>
);
}
export const steps = ["#login", "#logout"];
Keluaran sebenarnya
Teks awal: Status: Belum masuk
Setelah klik ke-1: Status: Halo Budi
Setelah klik ke-2: Status: Belum masuk
Contoh 8: Halaman yang memerlukan masuk
Jika tidak ada pengguna, halaman meminta pengunjung untuk masuk. Jika ada pengguna, halaman menampilkan dasbor. Inilah gagasan di balik rute yang dilindungi.
1. Masuk, lalu keluar
import React, { useState } from "react";
// Halaman yang harus masuk dulu baru bisa dilihat. Jika belum ada pengguna, halaman menampilkan pesan agar masuk terlebih dahulu
function Dashboard({ user }) {
if (!user) return <p data-out>Silakan masuk terlebih dahulu</p>;
return <p data-out>Halaman dasbor milik {user}</p>;
}
export function App() {
const [user, setUser] = useState(null);
return (
<div>
<button id="login" onClick={() => setUser("Budi")}>Masuk</button>
<button id="logout" onClick={() => setUser(null)}>Keluar</button>
<Dashboard user={user} />
</div>
);
}
export const steps = ["#login", "#logout"];
Keluaran sebenarnya
Teks awal: Silakan masuk terlebih dahulu
Setelah klik ke-1: Halaman dasbor milik Budi
Setelah klik ke-2: Silakan masuk terlebih dahulu
Contoh 9: Halaman login lengkap
Halaman akhir menggabungkan formulir, kolom dengan petunjuk autocomplete yang benar, opsi ingat saya, dan tautan lupa kata sandi.
import React from "react";
// Halaman masuk lengkap yang menggabungkan semua bagian: formulir, kolom isian, opsi ingat saya, dan tautan lupa kata sandi
export function App() {
return (
<main>
<h1>Masuk</h1>
<form>
<label>Email<input type="email" name="email" autoComplete="username" /></label>
<label>Kata sandi<input type="password" name="password" autoComplete="current-password" /></label>
<label><input type="checkbox" name="remember" /> Ingat saya</label>
<button type="submit">Masuk</button>
</form>
<a href="/forgot">Lupa kata sandi?</a>
</main>
);
}
Keluaran sebenarnya
<main><h1>Masuk</h1><form><label>Email<input type="email" name="email" autoComplete="username"/></label><label>Kata sandi<input type="password" name="password" autoComplete="current-password"/></label><label><input type="checkbox" name="remember"/> Ingat saya</label><button type="submit">Masuk</button></form><a href="/forgot">Lupa kata sandi?</a></main>
Langkah berikutnya
Berikutnya, hubungkan halaman login ke API sungguhan dengan fetch, gunakan cookie HTTP-only atau token yang aman, dan tambahkan alur lupa kata sandi yang mengirim tautan reset melalui email.
Ringkasan
Halaman login yang baik menggabungkan formulir yang dikendalikan oleh state, validasi, batas untuk kegagalan berulang, status pengguna bersama dengan Context, dan halaman yang memeriksa masuk sebelum menampilkan data. Sistem sungguhan harus selalu memeriksa akses di server.