ReactJS #13: Fetch API Part 3 – Menangani Loading dan Error


ReactJS #13: Fetch API Part 3

Menangani Loading dan Error
(Sabar Ya, Lagi Ambil Data)

Halo, sobat sabar! 🧘

Pernahkah kamu memesan makanan di restoran? Kamu menunggu beberapa saat sebelum makanan datang. Kadang makanan datang, kadang keliru, kadang kehabisan. Nah, saat aplikasi kita mengambil data dari internet, pengguna juga perlu tahu apa yang terjadi: apakah data sedang diambil, apakah berhasil, atau apakah ada masalah. Di artikel ini, kita akan belajar membuat pengalaman menunggu yang menyenangkan dan menangani error dengan ramah.

🍽️ ➡️ ⏳ ➡️ 🍔 atau ❌
Seperti menunggu makanan: ada loading, ada sukses, ada error

🔄 1. Indikator Loading yang Keren

Sebelumnya kita hanya menampilkan teks "Memuat data...". Itu sudah cukup, tapi kita bisa membuatnya lebih menarik dengan spinner (lingkaran berputar) atau skeleton (kerangka yang berkedip).

⚪ Spinner (Lingkaran Berputar)

Kita buat spinner dengan CSS. Tambahkan kode ini di <style>:

.spinner { border: 8px solid #f3f3f3; border-top: 8px solid #f7b731; border-radius: 50%; width: 60px; height: 60px; animation: spin 1s linear infinite; margin: 20px auto; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }

Lalu di komponen, kita bisa menggunakannya:

if (loading) { return ( <div className="loading-container"> <div className="spinner"></div> <p>Sabar ya, lagi ambil data...</p> </div> ); }

🦴 Skeleton (Kerangka Berkedip)

Skeleton adalah tiruan tampilan sebelum data muncul. Biasanya berupa kotak abu-abu yang berkedip. Ini memberi kesan bahwa sesuatu akan segera muncul.

.skeleton-card { background-color: #e0e0e0; border-radius: 40px; padding: 20px; width: 250px; height: 150px; animation: pulse 1.5s ease-in-out infinite; } @keyframes pulse { 0% { opacity: 0.6; } 50% { opacity: 1; } 100% { opacity: 0.6; } }

Contoh penggunaan untuk menampilkan 4 skeleton card:

if (loading) { return ( <div> <h3>👥 Memuat pengguna...</h3> <div className="card-container"> {[1, 2, 3, 4].map(n => ( <div key={n} className="skeleton-card"></div> ))} </div> </div> ); }

❌ 2. Menangani Error dengan Ramah

Koneksi internet bisa putus, server bisa error. Kita harus memberi tahu pengguna dengan cara yang tidak membuat mereka frustrasi. Tambahkan tombol "Coba Lagi" agar mereka bisa mengulang permintaan.

Contoh komponen dengan error handling dan tombol coba lagi:

function DaftarPengguna() { const [users, setUsers] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); async function ambilData() { setLoading(true); setError(null); try { const res = await fetch('https://jsonplaceholder.typicode.com/users'); if (!res.ok) throw new Error(`Gagal: ${res.status}`); const data = await res.json(); setUsers(data); } catch (err) { setError(err.message); } finally { setLoading(false); } } useEffect(() => { ambilData(); }, []); if (loading) { return ( <div className="loading-container"> <div className="spinner"></div> <p>Sabar ya, lagi ambil data...</p> </div> ); } if (error) { return ( <div className="error-container"> <div style={{ fontSize: '3em' }}>😵</div> <h3>Aduh, ada masalah!</h3> <p>{error}</p> <button onClick={ambilData}>🔄 Coba Lagi</button> </div> ); } return ( ... ); // tampilkan data }

Penjelasan:

  • Kita menggunakan try/catch/finally untuk menangkap error.
  • Di bagian error, kita tampilkan pesan yang ramah dan tombol untuk mencoba lagi.
  • Fungsi ambilData bisa dipanggil ulang saat tombol diklik.
💡 Tips: Gunakan try/catch dengan async/await agar kode lebih rapi daripada .then().catch(). Pilih yang paling kamu suka!

🔄 3. Menggabungkan Loading, Error, dan Data

Sekarang kita buat komponen lengkap yang menampilkan data pengguna dalam kartu, dengan spinner saat loading, dan pesan error jika gagal. Kita juga tambahkan tombol "Coba Lagi".

import { useState, useEffect } from 'react'; function DaftarPenggunaLengkap() { const [users, setUsers] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); async function ambilData() { setLoading(true); setError(null); try { const res = await fetch('https://jsonplaceholder.typicode.com/users'); if (!res.ok) throw new Error('Gagal mengambil data'); const data = await res.json(); setUsers(data); } catch (err) { setError(err.message); } finally { setLoading(false); } } useEffect(() => { ambilData(); }, []); if (loading) { return ( <div className="loading-container"> <div className="spinner"></div> <p>Sabar ya, lagi ambil data pengguna...</p> </div> ); } if (error) { return ( <div className="error-container"> <div style={{ fontSize: '3em' }}>😵</div> <h3>Yah, ada yang error!</h3> <p>{error}</p> <button onClick={ambilData}>🔄 Coba Lagi</button> </div> ); } return ( <div> <h3>👥 Daftar Pengguna (dengan loading & error keren)</h3> <div className="card-container"> {users.map(user => ( <div key={user.id} className="card"> <h4>{user.name}</h4> <p>📧 {user.email}</p> <p>🏙️ {user.address.city}</p> </div> ))} </div> </div> ); }

🧪 Latihan: Aplikasi Kutipan dengan Loading dan Error

Buat komponen KutipanHarian yang:

  1. Mengambil kutipan acak dari https://api.quotable.io/random.
  2. Menampilkan spinner saat loading.
  3. Jika error, tampilkan pesan error dan tombol coba lagi.
  4. Tambahkan tombol "Kutipan Lainnya" untuk mengambil kutipan baru (jangan lupa set loading dan error ulang).
  5. Nonaktifkan tombol saat loading (gunakan atribut disabled).

Kerangka (coba selesaikan sendiri):

import { useState, useEffect } from 'react'; function KutipanHarian() { const [kutipan, setKutipan] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); async function ambilKutipan() { setLoading(true); setError(null); try { const res = await fetch('https://api.quotable.io/random'); if (!res.ok) throw new Error('Gagal ambil kutipan'); const data = await res.json(); setKutipan(data); } catch (err) { setError(err.message); } finally { setLoading(false); } } useEffect(() => { ambilKutipan(); }, []); // TODO: tampilkan loading (spinner) // TODO: tampilkan error dengan tombol coba lagi // TODO: tampilkan kutipan dan tombol "Kutipan Lainnya" (nonaktifkan saat loading) return ( ... ); }

Jangan lupa untuk menonaktifkan tombol "Kutipan Lainnya" saat loading true agar tidak diklik dua kali.

💡 Jawaban (bagian tombol):
<button onClick={ambilKutipan} disabled={loading}>🔄 Kutipan Lainnya</button>

🎯 4. Tips Membuat Pengalaman Pengguna yang Baik

  • Jangan biarkan pengguna menunggu tanpa informasi. Selalu tampilkan indikator loading.
  • Gunakan skeleton untuk data yang sudah diketahui bentuknya. Ini memberi ilusi bahwa sesuatu akan segera muncul.
  • Pesan error yang ramah, bukan kode error mentah. "Yah, koneksimu putus nih" lebih baik daripada "Error: NetworkError".
  • Berikan tombol "Coba Lagi" untuk memulihkan dari error. Jangan paksa pengguna me-refresh halaman.
  • Nonaktifkan tombol aksi saat loading untuk mencegah pengiriman ganda.

🚀 Kesimpulan

  • Loading bisa dibuat menarik dengan spinner (lingkaran berputar) atau skeleton (kerangka berkedip).
  • Error harus ditampilkan dengan ramah dan memberi kesempatan pengguna untuk mencoba lagi.
  • Gunakan try/catch/finally untuk menangani proses async dengan rapi.
  • State loading dan error adalah sahabat terbaik dalam pengambilan data.
  • Selalu pikirkan pengalaman pengguna (UX) agar aplikasi kita nyaman dipakai.

Di artikel selanjutnya kita akan membuat Aplikasi Cuaca Sederhana dengan API sungguhan. Kita akan menggabungkan semua yang sudah dipelajari: fetch, loading, error, dan tampilan yang cantik. Sampai jumpa!

Lebih baru Lebih lama

نموذج الاتصال