Kenapa ini penting
Antarmuka yang hidup selalu berubah sesuai keadaan: tombol "Login" berganti "Logout" setelah masuk, pesan error muncul saat form salah, spinner tampil saat data dimuat. Di sisi lain, perubahan itu dipicu oleh aksi pengguna: klik, ketik, pilih. Modul ini menggabungkan dua hal itu: conditional rendering (menampilkan elemen sesuai kondisi) dan event handling (merespons aksi pengguna), termasuk pola controlled input yang menjadi standar membuat form di React.
Konsep inti
Conditional rendering: empat pola utama
| Pola | Kapan dipakai | Contoh |
|---|---|---|
if + early return |
Seluruh tampilan berbeda total | if (loading) return <Spinner />; |
Ternary ? : |
Pilih salah satu dari dua tampilan | {isLogin ? <Logout /> : <Login />} |
&& |
Tampilkan sesuatu atau tidak sama sekali | {error && <p>{error}</p>} |
| Variabel JSX | Logika agak panjang, agar JSX tetap bersih | let konten; if (...) konten = <A />; |
Early return artinya komponen langsung mengembalikan JSX lain di awal fungsi untuk kondisi khusus, sehingga sisa kode tidak perlu memikirkan kondisi tersebut. Analogi: satpam di pintu masuk. Kalau tidak punya tiket, langsung disuruh pulang, tidak perlu masuk ke dalam gedung.
Komponen juga boleh mengembalikan null untuk tidak menampilkan apa pun.
Jebakan angka 0 pada &&
{jumlah && <p>Ada {jumlah} pesan</p>} terlihat benar, tapi saat jumlah bernilai 0, ekspresi 0 && ... menghasilkan 0, dan React menampilkan angka 0 di layar. Pastikan sisi kiri && selalu boolean: {jumlah > 0 && ...}.
Event handling
Event di React ditulis camelCase dan menerima fungsi, bukan string: <button onClick={handleKlik}>. Perhatikan perbedaannya:
onClick={handleKlik}→ benar, mengirim fungsi.onClick={handleKlik()}→ salah, memanggil fungsi saat render.onClick={() => handleKlik(id)}→ benar, membungkus agar bisa mengirim argumen.
Handler menerima objek event (e). Yang sering dipakai: e.target.value (nilai input), e.target.checked (checkbox), e.preventDefault() (mencegah form reload halaman), dan e.stopPropagation() (mencegah event naik ke elemen induk).
Event umum: onClick, onChange, onSubmit, onKeyDown, onFocus, onBlur, onMouseEnter.
Controlled input
Di HTML biasa, input menyimpan nilainya sendiri di DOM. Di React, pola yang dianjurkan adalah controlled input: nilai input dikendalikan state.
value={state}membuat input selalu menampilkan isi state.onChange={e => setState(e.target.value)}memperbarui state setiap kali pengguna mengetik.
Dengan begitu state adalah satu-satunya sumber kebenaran (single source of truth). Kamu bisa memvalidasi, memformat (misalnya otomatis huruf besar), atau mengosongkan input kapan saja hanya dengan mengubah state.
Analogi: input biasa seperti buku catatan orang lain; kamu harus meminjamnya untuk tahu isinya. Controlled input seperti papan tulis di kelasmu sendiri; kamu selalu tahu isinya dan bisa menghapusnya kapan saja.
| Elemen | Prop nilai | Diambil dari event |
|---|---|---|
<input type="text"> |
value |
e.target.value |
<textarea> |
value |
e.target.value |
<select> |
value |
e.target.value |
<input type="checkbox"> |
checked |
e.target.checked |
Perhatian: jika kamu memberi value tanpa onChange, input menjadi read-only dan React memberi peringatan.
Contoh kode
// src/App.jsx, form login dengan semua pola conditional rendering dan controlled input
import { useState } from 'react';
function Dashboard({ nama, onLogout }) {
return (
<section>
<h2>Selamat datang, {nama}!</h2>
<button onClick={onLogout}>Logout</button>
</section>
);
}
function Notifikasi({ jumlah }) {
// Kembalikan null = tidak menampilkan apa-apa
if (jumlah === 0) return null;
return <p>🔔 Kamu punya {jumlah} notifikasi baru</p>;
}
export default function App() {
const [user, setUser] = useState(null);
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [ingat, setIngat] = useState(false);
const [error, setError] = useState('');
const [jumlahNotif] = useState(0);
function handleSubmit(e) {
e.preventDefault(); // cegah halaman reload
if (!email.includes('@')) {
setError('Format email tidak valid');
return;
}
if (password.length < 6) {
setError('Password minimal 6 karakter');
return;
}
setError('');
setUser({ nama: email.split('@')[0], ingat });
}
// Early return: kalau sudah login, tampilkan dashboard saja
if (user) {
return (
<>
<Dashboard nama={user.nama} onLogout={() => setUser(null)} />
<Notifikasi jumlah={jumlahNotif} />
{/* ❌ {jumlahNotif && <p>...</p>} akan menampilkan "0" */}
{jumlahNotif > 0 && <p>Cek kotak masuk.</p>}
</>
);
}
return (
<form onSubmit={handleSubmit}>
<h2>Login</h2>
<input
type="email"
value={email}
onChange={e => setEmail(e.target.value.trim())}
placeholder="email@contoh.com"
/>
<input
type="password"
value={password}
onChange={e => setPassword(e.target.value)}
placeholder="Password"
/>
<label>
<input type="checkbox" checked={ingat} onChange={e => setIngat(e.target.checked)} />
Ingat saya
</label>
{/* && untuk tampil/tidak */}
{error && <p style={{ color: 'crimson' }}>{error}</p>}
{/* Ternary untuk memilih teks tombol */}
<button type="submit" disabled={!email || !password}>
{email && password ? 'Masuk' : 'Isi email & password'}
</button>
<p>Panjang password: {password.length} karakter</p>
</form>
);
}
// Ketik "budi" lalu submit → "Format email tidak valid"
// Ketik "budi@mail.com" + "rahasia1" → "Selamat datang, budi!"
Event lain dan objek event:
import { useState } from 'react';
export function KotakEvent() {
const [log, setLog] = useState('');
const [kota, setKota] = useState('jakarta');
return (
<div onClick={() => setLog('Div induk diklik')} style={{ padding: 16, background: '#f1f5f9' }}>
<button
onClick={e => {
e.stopPropagation(); // tanpa ini, onClick div juga terpanggil
setLog('Tombol diklik');
}}
>
Klik aku
</button>
<input onKeyDown={e => e.key === 'Enter' && setLog('Enter ditekan: ' + e.target.value)} />
<select value={kota} onChange={e => setKota(e.target.value)}>
<option value="jakarta">Jakarta</option>
<option value="surabaya">Surabaya</option>
<option value="makassar">Makassar</option>
</select>
<p>{log} | Kota: {kota}</p>
</div>
);
}
Kesalahan umum
- Memanggil handler saat render.
onClick={hapus(id)}langsung menjalankanhapus. PakaionClick={() => hapus(id)}. - Angka 0 muncul di layar karena
&&. Ubah sisi kiri jadi boolean:{list.length > 0 && ...}. - Lupa
e.preventDefault()padaonSubmit. Halaman reload dan state hilang. Panggil di baris pertama handler submit. - Memberi
valuetanpaonChange. Input tidak bisa diketik. TambahkanonChange, atau pakaidefaultValuekalau memang ingin uncontrolled. - Memakai
valueuntuk checkbox. Checkbox memakaicheckeddane.target.checked.
Latihan
- Mudah, Buat tombol "Tampilkan detail" yang menampilkan/menyembunyikan paragraf deskripsi produk memakai
&&, dan teks tombol berganti memakai ternary. - Sedang, Buat input "Nama" yang controlled. Tampilkan pesan "Nama terlalu pendek" (merah) jika panjangnya 1-2 karakter, "Nama valid ✓" (hijau) jika 3 ke atas, dan tidak ada pesan jika kosong.
- Menantang, Buat komponen
StatusPesanandengan propstatus('loading','error','kosong','sukses') yang memakai early return untuk setiap status, dan sebuahselectdiAppuntuk mengganti status.
Lihat solusi
1.
import { useState } from 'react';
export default function Detail() {
const [buka, setBuka] = useState(false);
return (
<>
<button onClick={() => setBuka(b => !b)}>{buka ? 'Sembunyikan detail' : 'Tampilkan detail'}</button>
{buka && <p>Kopi arabika Gayo, sangrai medium, notes cokelat dan jeruk.</p>}
</>
);
}
2.
import { useState } from 'react';
export default function InputNama() {
const [nama, setNama] = useState('');
let pesan = null;
if (nama.length > 0 && nama.length < 3) {
pesan = <p style={{ color: 'crimson' }}>Nama terlalu pendek</p>;
} else if (nama.length >= 3) {
pesan = <p style={{ color: 'green' }}>Nama valid ✓</p>;
}
return (
<>
<input value={nama} onChange={e => setNama(e.target.value)} placeholder="Nama" />
{pesan}
</>
);
}
3.
import { useState } from 'react';
function StatusPesanan({ status }) {
if (status === 'loading') return <p>⏳ Memuat pesanan...</p>;
if (status === 'error') return <p style={{ color: 'crimson' }}>Gagal memuat pesanan.</p>;
if (status === 'kosong') return <p>Belum ada pesanan.</p>;
return <p>✅ 3 pesanan ditemukan.</p>;
}
export default function App() {
const [status, setStatus] = useState('loading');
return (
<>
<select value={status} onChange={e => setStatus(e.target.value)}>
<option value="loading">loading</option>
<option value="error">error</option>
<option value="kosong">kosong</option>
<option value="sukses">sukses</option>
</select>
<StatusPesanan status={status} />
</>
);
}
Ringkasan
- Conditional rendering memakai
if/early return, ternary,&&, atau variabel JSX; komponen boleh mengembalikannull. - Hindari angka
0tampil di layar dengan memastikan sisi kiri&&bernilai boolean. - Event handler menerima fungsi (
onClick={fn}atauonClick={() => fn(arg)}), bukan hasil pemanggilan fungsi. - Pakai
e.preventDefault()pada submit form dane.stopPropagation()untuk menahan event naik ke induk. - Controlled input:
valuedari state +onChangeyang memperbarui state; checkbox memakaichecked.