Bab 4: Kamu Mungkin Tidak Butuh Effect
⏱ 4 menit bacaKenapa Bab Ini Penting?
Ini mungkin bab paling penting di seluruh seri "Escape Hatches". Kenapa? Karena kebanyakan pemula React terlalu sering pakai useEffect. Mereka pakai Effect buat hal-hal yang sebenernya bisa dilakuin dengan cara yang lebih simpel dan lebih performa.
Bayangin gini: kamu punya palu (useEffect). Tiba-tiba semua masalah keliatan kayak paku. Mau masak? Palu. Mau nyetir? Palu. Mau nulis? Palu. Padahal banyak masalah yang butuh alat lain.
Aturan emas: Kalau sesuatu bisa dihitung dari props atau state yang ada, jangan simpen di state lain dan jangan pakai Effect. Hitung langsung saat render.
Anti-Pattern 1: Transformasi Data untuk Render
Ini yang paling sering terjadi. Kamu punya data, mau ditransformasi sebelum ditampilin:
// ❌ ANTI-PATTERN: pakai Effect buat transformasi data
function DaftarProduk({ produk }) {
const [produkMurah, setProdukMurah] = useState([]);
useEffect(() => {
// Filter produk yang harganya di bawah 50rb
const hasil = produk.filter(p => p.harga < 50000);
setProdukMurah(hasil);
}, [produk]);
return (
<ul>
{produkMurah.map(p => <li key={p.id}>{p.nama}</li>)}
</ul>
);
}Kenapa ini jelek?
- Render pertama:
produkMurahmasih[](kosong) - Effect jalan setelah render → update
produkMurah - Komponen render LAGI buat nampilin data yang baru
- Total: 2 render padahal harusnya cukup 1!
// ✅ BENAR: hitung langsung saat render
function DaftarProduk({ produk }) {
// Langsung hitung, nggak perlu state tambahan
const produkMurah = produk.filter(p => p.harga < 50000);
return (
<ul>
{produkMurah.map(p => <li key={p.id}>{p.nama}</li>)}
</ul>
);
}Satu render, langsung beres. Simpel, cepat, nggak ada state tambahan yang harus di-maintain.
Anti-Pattern 2: Menghitung Nilai Turunan
// ❌ ANTI-PATTERN: state + Effect buat nilai yang bisa dihitung
function Keranjang({ items }) {
const [totalHarga, setTotalHarga] = useState(0);
const [jumlahItem, setJumlahItem] = useState(0);
useEffect(() => {
setTotalHarga(items.reduce((sum, item) => sum + item.harga * item.qty, 0));
setJumlahItem(items.reduce((sum, item) => sum + item.qty, 0));
}, [items]);
return (
<div>
<p>Total item: {jumlahItem}</p>
<p>Total harga: Rp{totalHarga.toLocaleString()}</p>
</div>
);
}
// ✅ BENAR: hitung langsung
function Keranjang({ items }) {
// Dihitung setiap render, tapi itu OK! Ini cepat.
const totalHarga = items.reduce((sum, item) => sum + item.harga * item.qty, 0);
const jumlahItem = items.reduce((sum, item) => sum + item.qty, 0);
return (
<div>
<p>Total item: {jumlahItem}</p>
<p>Total harga: Rp{totalHarga.toLocaleString()}</p>
</div>
);
}"Tapi kan itu dihitung ulang setiap render?"
Ya! Dan itu nggak masalah untuk kebanyakan kasus. Array.reduce di array 100 item itu cuma butuh microsecond. Jauh lebih cepat daripada render ulang yang disebabkan Effect.
Anti-Pattern 3: Reset State Saat Props Berubah
// ❌ ANTI-PATTERN: reset state pakai Effect
function FormProfil({ userId }) {
const [nama, setNama] = useState('');
const [email, setEmail] = useState('');
// Reset form setiap userId berubah
useEffect(() => {
setNama('');
setEmail('');
}, [userId]);
return (
<form>
<input value={nama} onChange={e => setNama(e.target.value)} />
<input value={email} onChange={e => setEmail(e.target.value)} />
</form>
);
}Masalahnya: Render pertama dengan userId baru masih nunjukin data lama (sebelum Effect jalan). Baru setelah Effect jalan, form di-reset dan render lagi.
// ✅ BENAR: pakai key buat reset seluruh komponen
function HalamanProfil({ userId }) {
// key berubah = React unmount komponen lama, mount yang baru (state fresh)
return <FormProfil key={userId} userId={userId} />;
}
function FormProfil({ userId }) {
const [nama, setNama] = useState('');
const [email, setEmail] = useState('');
// Nggak perlu Effect! State otomatis fresh karena key berubah
return (
<form>
<input value={nama} onChange={e => setNama(e.target.value)} />
<input value={email} onChange={e => setEmail(e.target.value)} />
</form>
);
}Trik key: Kalau kamu kasih key yang berbeda ke komponen, React anggap itu komponen yang beda. Jadi dia unmount yang lama dan mount yang baru dengan state fresh. Nggak perlu Effect sama sekali!
Anti-Pattern 4: Fetch di Effect yang Harusnya di Event Handler
// ❌ ANTI-PATTERN: fetch saat form submit pakai Effect
function FormPesanan() {
const [pesanan, setPesanan] = useState(null);
const [kirim, setKirim] = useState(false);
useEffect(() => {
if (kirim) {
fetch('/api/pesanan', {
method: 'POST',
body: JSON.stringify(pesanan)
});
setKirim(false);
}
}, [kirim, pesanan]);
function handleSubmit() {
setPesanan({ item: 'Nasi Goreng', qty: 2 });
setKirim(true); // Trigger Effect
}
return <button onClick={handleSubmit}>Pesan</button>;
}Kenapa ini salah? Karena mengirim pesanan itu respons langsung dari aksi user (klik tombol). Ini bukan sinkronisasi. Ini event handling!
// ✅ BENAR: fetch langsung di event handler
function FormPesanan() {
async function handleSubmit() {
const pesanan = { item: 'Nasi Goreng', qty: 2 };
const res = await fetch('/api/pesanan', {
method: 'POST',
body: JSON.stringify(pesanan)
});
if (res.ok) {
alert('Pesanan berhasil!');
}
}
return <button onClick={handleSubmit}>Pesan</button>;
}Lebih simpel, lebih jelas, lebih mudah di-debug.
Anti-Pattern 5: Sinkronisasi Dua State
// ❌ ANTI-PATTERN: sinkronisasi state satu sama lain pakai Effect
function PilihProvinsiKota() {
const [provinsi, setProvinsi] = useState('');
const [kota, setKota] = useState('');
const [daftarKota, setDaftarKota] = useState([]);
// Reset kota setiap provinsi berubah
useEffect(() => {
setKota(''); // Reset kota
// Ini trigger render tambahan!
}, [provinsi]);
// Fetch daftar kota berdasarkan provinsi
useEffect(() => {
if (provinsi) {
fetch(`/api/kota?provinsi=${provinsi}`)
.then(res => res.json())
.then(data => setDaftarKota(data));
}
}, [provinsi]);
// ...
}
// ✅ BENAR: reset di event handler langsung
function PilihProvinsiKota() {
const [provinsi, setProvinsi] = useState('');
const [kota, setKota] = useState('');
const [daftarKota, setDaftarKota] = useState([]);
function handleProvinsiChange(e) {
const provinsiBaru = e.target.value;
setProvinsi(provinsiBaru);
setKota(''); // Reset kota langsung di sini!
// Fetch daftar kota
fetch(`/api/kota?provinsi=${provinsiBaru}`)
.then(res => res.json())
.then(data => setDaftarKota(data));
}
return (
<div>
<select value={provinsi} onChange={handleProvinsiChange}>
<option value="">Pilih Provinsi</option>
<option value="jawa-barat">Jawa Barat</option>
<option value="jawa-tengah">Jawa Tengah</option>
</select>
<select value={kota} onChange={e => setKota(e.target.value)}>
<option value="">Pilih Kota</option>
{daftarKota.map(k => (
<option key={k.id} value={k.id}>{k.nama}</option>
))}
</select>
</div>
);
}Kapan useMemo Diperlukan?
Kalau perhitungan kamu beneran mahal (ribuan item, operasi kompleks), baru pakai useMemo:
import { useMemo } from 'react';
function DaftarBesar({ items, filter }) {
// ❌ Tanpa memo: dihitung ulang setiap render (meskipun filter nggak berubah)
// const hasilFilter = items.filter(item => cocok(item, filter));
// ✅ Dengan useMemo: cuma dihitung ulang kalau items atau filter berubah
const hasilFilter = useMemo(() => {
console.log('Menghitung ulang filter...');
return items.filter(item => {
// Simulasi operasi mahal
return item.nama.toLowerCase().includes(filter.toLowerCase());
});
}, [items, filter]);
return (
<ul>
{hasilFilter.map(item => (
<li key={item.id}>{item.nama}</li>
))}
</ul>
);
}Kapan pakai useMemo?
- Array/object dengan ribuan item yang di-filter/sort
- Perhitungan matematika yang kompleks
- Bikin tree/graph structure dari data mentah
Kapan NGGAK perlu useMemo?
- Filter array kecil (< 100 item)
- Operasi string sederhana
- Penjumlahan/pengurangan biasa
- Kalau kamu nggak yakin, jangan pakai dulu. Optimasi prematur itu musuh.
Decision Tree: Effect vs Event Handler vs Langsung
Pakai flowchart ini buat menentukan di mana naruh logic:
Apakah ini respons langsung dari aksi user (klik, ketik, submit)?
├── YA → Taruh di EVENT HANDLER
│
└── TIDAK → Apakah ini perlu sinkronisasi dengan sistem luar?
│ (API, WebSocket, DOM, timer, localStorage)
│
├── YA → Taruh di useEffect
│
└── TIDAK → Apakah ini bisa dihitung dari state/props yang ada?
│
├── YA → HITUNG LANGSUNG saat render (atau useMemo kalau mahal)
│
└── TIDAK → Mungkin kamu butuh state baru
Contoh Lengkap: Refactoring dari Effect ke Tanpa Effect
Mari kita refactor komponen yang penuh Effect jadi lebih bersih:
// ❌ SEBELUM: penuh Effect yang nggak perlu
function TokoOnline({ produk, kategoriAktif, urutkan }) {
const [produkFiltered, setProdukFiltered] = useState([]);
const [produkSorted, setProdukSorted] = useState([]);
const [totalHarga, setTotalHarga] = useState(0);
const [jumlahProduk, setJumlahProduk] = useState(0);
// Effect 1: filter berdasarkan kategori
useEffect(() => {
const hasil = produk.filter(p =>
kategoriAktif === 'semua' || p.kategori === kategoriAktif
);
setProdukFiltered(hasil);
}, [produk, kategoriAktif]);
// Effect 2: sort setelah filter
useEffect(() => {
const sorted = [...produkFiltered].sort((a, b) => {
if (urutkan === 'harga-asc') return a.harga - b.harga;
if (urutkan === 'harga-desc') return b.harga - a.harga;
return a.nama.localeCompare(b.nama);
});
setProdukSorted(sorted);
}, [produkFiltered, urutkan]);
// Effect 3: hitung total
useEffect(() => {
setTotalHarga(produkSorted.reduce((sum, p) => sum + p.harga, 0));
setJumlahProduk(produkSorted.length);
}, [produkSorted]);
return (
<div>
<p>{jumlahProduk} produk, total Rp{totalHarga.toLocaleString()}</p>
<ul>
{produkSorted.map(p => (
<li key={p.id}>{p.nama} - Rp{p.harga.toLocaleString()}</li>
))}
</ul>
</div>
);
}Kode di atas punya 3 Effect dan 4 state tambahan yang semuanya nggak perlu! Setiap kali produk berubah, komponen render 4 kali (render awal + 3 Effect yang masing-masing trigger render).
// ✅ SESUDAH: bersih, cepat, mudah dipahami
function TokoOnline({ produk, kategoriAktif, urutkan }) {
// Semua dihitung langsung dari props. Nggak perlu state tambahan!
const produkFiltered = produk.filter(p =>
kategoriAktif === 'semua' || p.kategori === kategoriAktif
);
const produkSorted = [...produkFiltered].sort((a, b) => {
if (urutkan === 'harga-asc') return a.harga - b.harga;
if (urutkan === 'harga-desc') return b.harga - a.harga;
return a.nama.localeCompare(b.nama);
});
const totalHarga = produkSorted.reduce((sum, p) => sum + p.harga, 0);
const jumlahProduk = produkSorted.length;
return (
<div>
<p>{jumlahProduk} produk, total Rp{totalHarga.toLocaleString()}</p>
<ul>
{produkSorted.map(p => (
<li key={p.id}>{p.nama} - Rp{p.harga.toLocaleString()}</li>
))}
</ul>
</div>
);
}Dari 3 Effect + 4 state → 0 Effect + 0 state tambahan. Satu render, langsung beres.
Contoh: Validasi Form Tanpa Effect
// ❌ ANTI-PATTERN: validasi pakai Effect
function FormRegistrasi() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [errorEmail, setErrorEmail] = useState('');
const [errorPassword, setErrorPassword] = useState('');
const [formValid, setFormValid] = useState(false);
useEffect(() => {
if (!email.includes('@')) {
setErrorEmail('Email harus mengandung @');
} else {
setErrorEmail('');
}
}, [email]);
useEffect(() => {
if (password.length < 8) {
setErrorPassword('Password minimal 8 karakter');
} else {
setErrorPassword('');
}
}, [password]);
useEffect(() => {
setFormValid(errorEmail === '' && errorPassword === '' && email !== '' && password !== '');
}, [errorEmail, errorPassword, email, password]);
// 3 Effect, 3 state tambahan... berantakan!
}
// ✅ BENAR: hitung langsung
function FormRegistrasi() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
// Validasi dihitung langsung dari state yang ada
const errorEmail = email && !email.includes('@') ? 'Email harus mengandung @' : '';
const errorPassword = password && password.length < 8 ? 'Password minimal 8 karakter' : '';
const formValid = email !== '' && password !== '' && !errorEmail && !errorPassword;
function handleSubmit(e) {
e.preventDefault();
if (formValid) {
// Kirim ke server (di event handler, bukan Effect!)
fetch('/api/register', {
method: 'POST',
body: JSON.stringify({ email, password })
});
}
}
return (
<form onSubmit={handleSubmit}>
<div>
<input
type="email"
value={email}
onChange={e => setEmail(e.target.value)}
placeholder="Email"
/>
{errorEmail && <span style={{ color: 'red' }}>{errorEmail}</span>}
</div>
<div>
<input
type="password"
value={password}
onChange={e => setPassword(e.target.value)}
placeholder="Password"
/>
{errorPassword && <span style={{ color: 'red' }}>{errorPassword}</span>}
</div>
<button type="submit" disabled={!formValid}>Daftar</button>
</form>
);
}Kapan Effect MEMANG Diperlukan?
Jangan salah paham, Effect tetap punya tempatnya. Ini kasus-kasus yang memang butuh Effect:
1. Sinkronisasi dengan Sistem Luar
// ✅ Koneksi WebSocket = sistem luar
useEffect(() => {
const ws = new WebSocket(url);
ws.onmessage = handleMessage;
return () => ws.close();
}, [url]);2. Subscribe ke Event Browser
// ✅ Event listener = sistem luar
useEffect(() => {
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);3. Integrasi Library Non-React
// ✅ Library peta = sistem luar
useEffect(() => {
const map = new MapLibrary(containerRef.current);
map.setCenter(lat, lng);
return () => map.destroy();
}, [lat, lng]);4. Fetch Data Awal (tapi pertimbangkan library)
// ✅ Fetch data saat mount (tapi lebih baik pakai React Query/SWR)
useEffect(() => {
let aktif = true;
fetchData().then(data => {
if (aktif) setData(data);
});
return () => { aktif = false; };
}, []);Analogi: Warung Makan
Bayangin komponen React itu warung makan:
- Render = masak makanan sesuai pesanan (props/state)
- Hitung langsung = potong bawang sambil masak (bagian dari proses masak)
- Event handler = pelanggan minta tambah sambal (respons ke aksi spesifik)
- Effect = nyalain AC karena suhu ruangan berubah (sinkronisasi dengan kondisi luar)
Kamu nggak perlu "Effect" buat motong bawang. Itu bagian dari masak! Sama kayak kamu nggak perlu Effect buat filter array atau hitung total.
Checklist: Apakah Kamu Butuh Effect?
Sebelum nulis useEffect, tanya diri sendiri:
-
❓ "Bisa nggak ini dihitung langsung dari state/props yang ada?"
- Kalau ya → Hitung langsung, nggak perlu Effect
-
❓ "Apakah ini respons dari aksi user?"
- Kalau ya → Taruh di event handler
-
❓ "Apakah ini cuma perlu jalan sekali saat mount?"
- Pertimbangkan: apakah bisa dipindah ke level yang lebih tinggi? (route loader, server component)
-
❓ "Apakah ini beneran sinkronisasi dengan sistem di luar React?"
- Kalau ya → OK, pakai Effect
-
❓ "Apakah Effect ini cuma set state berdasarkan state/props lain?"
- Kalau ya → Hampir pasti nggak butuh Effect!
⚠️ Jebakan
Jebakan 1: Effect Chain (Rantai Effect)
// ❌ Effect yang trigger Effect yang trigger Effect...
function JebakanChain() {
const [a, setA] = useState(1);
const [b, setB] = useState(0);
const [c, setC] = useState(0);
useEffect(() => { setB(a * 2); }, [a]); // a berubah → set b
useEffect(() => { setC(b + 10); }, [b]); // b berubah → set c
// Render 3 kali! a→render→b→render→c→render
}
// ✅ Hitung langsung
function Benar() {
const [a, setA] = useState(1);
const b = a * 2; // Langsung
const c = b + 10; // Langsung
// Render 1 kali!
}Jebakan 2: Fetch yang Harusnya di Event Handler
// ❌ Pakai state flag buat trigger fetch
function Salah() {
const [harusFetch, setHarusFetch] = useState(false);
useEffect(() => {
if (harusFetch) {
fetch('/api/submit');
setHarusFetch(false);
}
}, [harusFetch]);
return <button onClick={() => setHarusFetch(true)}>Submit</button>;
}
// ✅ Fetch langsung di handler
function Benar() {
function handleSubmit() {
fetch('/api/submit');
}
return <button onClick={handleSubmit}>Submit</button>;
}Jebakan 3: useMemo yang Nggak Perlu
// ❌ Overkill: useMemo buat operasi murah
function Overkill({ nama }) {
const namaKapital = useMemo(() => {
return nama.toUpperCase(); // Ini cepet banget, nggak perlu memo
}, [nama]);
}
// ✅ Langsung aja
function Simpel({ nama }) {
const namaKapital = nama.toUpperCase(); // Nggak perlu memo
}Jebakan 4: Inisialisasi State dari Props Pakai Effect
// ❌ Pakai Effect buat inisialisasi
function Salah({ nilaiAwal }) {
const [nilai, setNilai] = useState(0);
useEffect(() => {
setNilai(nilaiAwal); // Render 2x!
}, []);
}
// ✅ Inisialisasi langsung di useState
function Benar({ nilaiAwal }) {
const [nilai, setNilai] = useState(nilaiAwal); // Render 1x!
}Ringkasan
- Jangan pakai Effect buat transformasi data yang bisa dihitung langsung
- Jangan pakai Effect buat respons ke aksi user (pakai event handler)
- Jangan pakai Effect buat reset state (pakai
key) - Jangan pakai Effect buat sinkronisasi state satu sama lain (hitung langsung)
- Pakai
useMemohanya kalau perhitungan beneran mahal - Pakai Effect hanya buat sinkronisasi dengan sistem di luar React
Prinsip: Kalau bisa dihitung, jangan disimpan. Kalau bisa di event handler, jangan di Effect.
🏋️ Challenge
Challenge 1: Refactor Effect Chains
Refactor komponen berikut yang punya 3 Effect berantai jadi tanpa Effect sama sekali:
function KonversiSuhu() {
const [celsius, setCelsius] = useState(0);
const [fahrenheit, setFahrenheit] = useState(32);
const [kategori, setKategori] = useState('dingin');
useEffect(() => {
setFahrenheit(celsius * 9/5 + 32);
}, [celsius]);
useEffect(() => {
if (fahrenheit < 50) setKategori('dingin');
else if (fahrenheit < 80) setKategori('nyaman');
else setKategori('panas');
}, [fahrenheit]);
}Hint: Semua bisa dihitung langsung dari celsius.
Lihat Solusi
import { useState } from 'react';
function KonversiSuhu() {
const [celsius, setCelsius] = useState(0);
// Hitung langsung, nggak perlu state atau Effect!
const fahrenheit = celsius * 9/5 + 32;
const kategori = fahrenheit < 50 ? 'dingin'
: fahrenheit < 80 ? 'nyaman'
: 'panas';
return (
<div>
<input
type="number"
value={celsius}
onChange={e => setCelsius(Number(e.target.value))}
/>
<span>°C</span>
<p>{celsius}°C = {fahrenheit.toFixed(1)}°F</p>
<p>Kategori: {kategori}</p>
<p style={{
color: kategori === 'dingin' ? 'blue' : kategori === 'panas' ? 'red' : 'green'
}}>
{kategori === 'dingin' && '🥶 Brrr... dingin!'}
{kategori === 'nyaman' && '😊 Suhu nyaman'}
{kategori === 'panas' && '🥵 Panas banget!'}
</p>
</div>
);
}Penjelasan: Dari 2 state + 2 Effect → 0 state tambahan + 0 Effect. fahrenheit dan kategori cuma turunan dari celsius, jadi nggak perlu disimpan terpisah.
Challenge 2: Search dengan Filter Tanpa Effect
Bikin komponen pencarian produk yang bisa filter berdasarkan kategori DAN keyword. Jangan pakai Effect sama sekali.
Hint: Filter dan search itu cuma transformasi data. Hitung langsung dari state.
Lihat Solusi
import { useState } from 'react';
const PRODUK = [
{ id: 1, nama: 'Laptop ASUS', kategori: 'elektronik', harga: 8000000 },
{ id: 2, nama: 'Kemeja Batik', kategori: 'fashion', harga: 150000 },
{ id: 3, nama: 'Mouse Wireless', kategori: 'elektronik', harga: 200000 },
{ id: 4, nama: 'Celana Jeans', kategori: 'fashion', harga: 300000 },
{ id: 5, nama: 'Keyboard Mechanical', kategori: 'elektronik', harga: 500000 },
{ id: 6, nama: 'Sepatu Sneakers', kategori: 'fashion', harga: 450000 },
{ id: 7, nama: 'Headphone Sony', kategori: 'elektronik', harga: 1200000 },
{ id: 8, nama: 'Tas Ransel', kategori: 'fashion', harga: 250000 },
];
function PencarianProduk() {
const [keyword, setKeyword] = useState('');
const [kategori, setKategori] = useState('semua');
const [urutkan, setUrutkan] = useState('nama');
// Semua dihitung langsung! Nggak perlu Effect!
const produkFiltered = PRODUK
.filter(p => kategori === 'semua' || p.kategori === kategori)
.filter(p => p.nama.toLowerCase().includes(keyword.toLowerCase()))
.sort((a, b) => {
if (urutkan === 'nama') return a.nama.localeCompare(b.nama);
if (urutkan === 'harga-asc') return a.harga - b.harga;
return b.harga - a.harga;
});
const totalHarga = produkFiltered.reduce((sum, p) => sum + p.harga, 0);
return (
<div>
<h2>Pencarian Produk</h2>
<input
value={keyword}
onChange={e => setKeyword(e.target.value)}
placeholder="Cari produk..."
/>
<select value={kategori} onChange={e => setKategori(e.target.value)}>
<option value="semua">Semua Kategori</option>
<option value="elektronik">Elektronik</option>
<option value="fashion">Fashion</option>
</select>
<select value={urutkan} onChange={e => setUrutkan(e.target.value)}>
<option value="nama">Urutkan: Nama</option>
<option value="harga-asc">Harga: Rendah ke Tinggi</option>
<option value="harga-desc">Harga: Tinggi ke Rendah</option>
</select>
<p>Ditemukan: {produkFiltered.length} produk (Total: Rp{totalHarga.toLocaleString()})</p>
<ul>
{produkFiltered.map(p => (
<li key={p.id}>
{p.nama} - Rp{p.harga.toLocaleString()} [{p.kategori}]
</li>
))}
</ul>
</div>
);
}Penjelasan: Nol Effect! Semua filter, sort, dan perhitungan total dilakukan langsung saat render. Setiap kali user ganti keyword/kategori/urutan, state berubah → komponen render → hasil baru dihitung langsung.
Challenge 3: Identifikasi Mana yang Butuh Effect
Dari daftar berikut, tentukan mana yang butuh Effect dan mana yang nggak. Jelaskan kenapa.
- Update document.title setiap halaman berubah
- Filter array berdasarkan input pencarian
- Koneksi ke WebSocket saat komponen mount
- Hitung diskon dari harga dan persentase
- Kirim data form ke server saat user klik Submit
- Subscribe ke window resize event
Lihat Solusi
-
Update document.title → ✅ BUTUH Effect
document.titleitu DOM API (sistem luar). React nggak punya cara deklaratif buat set title.
jsxuseEffect(() => { document.title = halaman; }, [halaman]); -
Filter array → ❌ NGGAK butuh Effect
- Ini cuma transformasi data. Hitung langsung.
jsxconst hasilFilter = items.filter(i => i.nama.includes(keyword)); -
Koneksi WebSocket → ✅ BUTUH Effect
- WebSocket itu sistem luar yang perlu setup dan cleanup.
jsxuseEffect(() => { const ws = new WebSocket(url); return () => ws.close(); }, [url]); -
Hitung diskon → ❌ NGGAK butuh Effect
- Ini cuma matematika dari data yang ada.
jsxconst hargaDiskon = harga * (1 - diskonPersen / 100); -
Kirim form saat Submit → ❌ NGGAK butuh Effect
- Ini respons ke aksi user. Taruh di event handler.
jsxfunction handleSubmit() { fetch('/api/submit', { ... }); } -
Subscribe resize event → ✅ BUTUH Effect
- Event listener browser itu sistem luar yang perlu setup dan cleanup.
jsxuseEffect(() => { window.addEventListener('resize', handler); return () => window.removeEventListener('resize', handler); }, []);
Ringkasan: Yang butuh Effect = interaksi dengan sistem di luar React (DOM API, WebSocket, event listener browser). Yang nggak = transformasi data, respons ke aksi user.
Sudah paham materi ini?
Tandai sebagai selesai untuk melacak progress-mu.