Mengambil Data: Loading, Error & Sukses
Pola fetch data dengan useEffect dan menangani tiga keadaan UI.
Pola tiga keadaan
Mengambil data dari server bersifat asynchronous dan bisa gagal. UI yang baik menangani:
- Loading — "Memuat...".
- Error — pesan gagal (dan opsi coba lagi).
- Sukses — menampilkan data.
Implementasi dasar
function DaftarUser({ ambil }) {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let batal = false; // cegah update setelah unmount
ambil()
.then((hasil) => { if (!batal) { setData(hasil); setLoading(false); } })
.catch(() => { if (!batal) { setError("Gagal memuat data"); setLoading(false); } });
return () => { batal = true; };
}, []);
if (loading) return <p className="loading">Memuat...</p>;
if (error) return <p className="error">{error}</p>;
return (
<ul>
{data.map((u) => <li key={u.id}>{u.nama}</li>)}
</ul>
);
}
Memakai fetch asli
useEffect(() => {
async function muat() {
try {
const res = await fetch("https://jsonplaceholder.typicode.com/users");
if (!res.ok) throw new Error("HTTP " + res.status);
setData(await res.json());
} catch (e) {
setError(e.message);
} finally {
setLoading(false);
}
}
muat();
}, []);
Perhatikan: useEffect tidak boleh langsung async; buat fungsi async di dalamnya lalu panggil.
Menerima fungsi pengambil data lewat props
Pada latihan ini, komponen menerima prop ambil (function yang mengembalikan Promise). Cara ini membuat komponen mudah diuji dan tidak terikat ke satu URL.
Race condition
Jika dependency berubah cepat (mis. pencarian), respons lama bisa tiba setelah respons baru. Variabel batal (atau AbortController) mencegah hasil basi menimpa hasil terbaru.
Library yang lebih baik
Untuk aplikasi nyata, gunakan TanStack Query (React Query) atau SWR: caching, retry, refetch otomatis, dan status loading/error siap pakai.
Tips UX
- Tampilkan skeleton alih-alih teks "Memuat..." untuk kesan lebih cepat.
- Selalu sediakan pesan error yang jelas.
- Tangani keadaan data kosong (
data.length === 0).