Cara Mengambil Data dari API di React
Pendahuluan
Bayangkan kamu sedang membangun aplikasi seperti Tokopedia. Setiap kali pengguna membuka halaman produk, aplikasi harus mengambil data produk terbaru dari server. Nah, proses “mengambil data dari server” inilah yang disebut fetching data dari API.
Di React, mengambil data dari API adalah salah satu skill paling penting yang perlu kamu kuasai. Hampir semua aplikasi nyata — dari daftar berita, data cuaca, sampai katalog produk — bergantung pada data yang datang dari luar aplikasi.
Artikel ini akan membahas cara mengambil data dari API menggunakan React dari nol. Kamu tidak perlu pengalaman sebelumnya dengan API — semua akan dijelaskan langkah demi langkah.
Prasyarat: Kamu sebaiknya sudah familiar dengan konsep dasar React seperti komponen dan state. Jika belum, baca dulu artikel tentang Pengenalan React: Memahami Komponen dan Props dan Memahami useState: Mengelola State di React sebelum masuk ke artikel ini.
Konsep Dasar
Apa Itu API?
API (Application Programming Interface) adalah “jembatan” antara aplikasi kamu dengan server. Anggap saja API seperti pelayan di restoran: kamu (aplikasi) memberikan pesanan (request), pelayan (API) pergi ke dapur (server), lalu membawa makanan (data) kembali ke mejamu.
Contoh nyata: jika kamu ingin menampilkan daftar pengguna di aplikasimu, kamu mengirim request ke URL seperti https://api.example.com/users, dan server mengembalikan data dalam format JSON.
Apa Itu JSON?
JSON (JavaScript Object Notation) adalah format data yang paling umum digunakan saat bekerja dengan API. Tampilannya seperti objek JavaScript biasa:
{
"id": 1,
"name": "Budi Santoso",
"email": "[email protected]"
}
Fetch API vs Axios
Ada dua cara populer untuk mengambil data di JavaScript:
| Fetch API | Axios | |
|---|---|---|
| Bawaan browser | ✅ Ya | ❌ Perlu install |
| Otomatis parse JSON | ❌ Manual | ✅ Otomatis |
| Error handling | Lebih verbose | Lebih simpel |
Untuk pemula, Fetch API adalah pilihan yang bagus karena sudah tersedia tanpa perlu install apapun. Artikel ini akan menggunakan Fetch API.
Siklus Hidup Data Fetching di React
Saat mengambil data dari API di React, ada tiga “state” yang perlu kamu kelola:
- Loading — Data sedang diambil dari server
- Success — Data berhasil didapat
- Error — Terjadi kesalahan saat mengambil data
Konsep ini sangat penting! Aplikasi yang baik selalu menangani ketiga kondisi ini agar pengguna tidak bingung.
Hook useEffect
Untuk menjalankan kode “efek samping” seperti fetching data, React menyediakan hook bernama useEffect. Hook ini berjalan setelah komponen selesai dirender.
useEffect(() => {
// Kode di sini berjalan setelah render
}, []); // Array kosong = hanya berjalan sekali saat komponen pertama muncul
useEffect menerima dua argumen:
- Fungsi callback — kode yang ingin dijalankan sebagai efek samping
- Dependency array — daftar nilai yang “diawasi”;
useEffectakan berjalan ulang setiap kali salah satu nilai ini berubah
Jika dependency array dikosongkan ([]), efek hanya berjalan satu kali saat komponen pertama kali ditampilkan. Inilah pola yang paling umum digunakan untuk fetching data awal.
Contoh Kode
Contoh 1: Fetching Data Sederhana
Mari mulai dengan contoh paling dasar — mengambil daftar pengguna dari API publik.
import { useState, useEffect } from 'react';
function DaftarPengguna() {
const [pengguna, setPengguna] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/users')
.then((response) => {
if (!response.ok) {
throw new Error('Gagal mengambil data');
}
return response.json();
})
.then((data) => {
setPengguna(data);
setLoading(false);
})
.catch((err) => {
setError(err.message);
setLoading(false);
});
}, []);
if (loading) return <p>Memuat data...</p>;
if (error) return <p>Error: {error}</p>;
return (
<ul>
{pengguna.map((user) => (
<li key={user.id}>{user.name} — {user.email}</li>
))}
</ul>
);
}
export default DaftarPengguna;
Penjelasan kode di atas:
useState([])— menyimpan data pengguna, awalnya array kosonguseState(true)— status loading, awalnyatruekarena kita langsung fetch saat komponen munculuseState(null)— menyimpan pesan error jika adauseEffectdengan array[]— fetch hanya dijalankan sekali saat komponen pertama kali dimuatresponse.ok— memastikan HTTP status dalam rentang 200–299 sebelum memproses data
Contoh 2: Menggunakan async/await (Lebih Bersih)
Cara di atas menggunakan .then() yang bisa membuat kode terlihat berlapis-lapis. Cara yang lebih modern dan mudah dibaca adalah menggunakan async/await:
import { useState, useEffect } from 'react';
function DaftarArtikel() {
const [artikel, setArtikel] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
// Perhatian: useEffect tidak bisa langsung async
// Jadi kita buat fungsi async di dalamnya
const ambilArtikel = async () => {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=5');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
setArtikel(data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
ambilArtikel();
}, []);
if (loading) {
return <div>Sedang memuat artikel...</div>;
}
if (error) {
return <div>Terjadi kesalahan: {error}</div>;
}
return (
<div>
<h2>Daftar Artikel</h2>
{artikel.map((post) => (
<div key={post.id} style={{ marginBottom: '16px', padding: '12px', border: '1px solid #ccc' }}>
<h3>{post.title}</h3>
<p>{post.body}</p>
</div>
))}
</div>
);
}
export default DaftarArtikel;
Penting:
useEffecttidak bisa langsung menerima fungsiasync. Itulah mengapa kita membuat fungsiambilArtikeldi dalamuseEffectdan memanggilnya secara terpisah. BlokfinallymemastikansetLoading(false)selalu dipanggil, baik fetch berhasil maupun gagal.
Contoh 3: Fetch Berdasarkan Input Pengguna
Skenario yang lebih realistis: pengguna mengetik sesuatu, lalu aplikasi mengambil data berdasarkan input tersebut. Bayangkan fitur pencarian seperti di aplikasi belanja online.
import { useState, useEffect } from 'react';
function CariPengguna() {
const [query, setQuery] = useState('');
const [hasil, setHasil] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
// Jangan fetch jika query kosong
if (!query.trim()) {
setHasil([]);
return;
}
const ambilData = async () => {
setLoading(true);
try {
const response = await fetch(
`https://jsonplaceholder.typicode.com/users?name_like=${encodeURIComponent(query)}`
);
if (!response.ok) {
throw new Error('Gagal mengambil data');
}
const data = await response.json();
setHasil(data);
} catch (err) {
console.error('Gagal mengambil data:', err);
} finally {
setLoading(false);
}
};
ambilData();
}, [query]); // Fetch ulang setiap kali 'query' berubah
return (
<div>
<input
type="text"
placeholder="Cari pengguna..."
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
{loading && <p>Mencari...</p>}
<ul>
{hasil.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
{!loading && query && hasil.length === 0 && (
<p>Tidak ada hasil untuk "{query}"</p>
)}
</div>
);
}
export default CariPengguna;
Di contoh ini, useEffect bergantung pada [query] — artinya setiap kali nilai query berubah, React akan menjalankan ulang kode di dalam useEffect. Pola ini sangat berguna untuk fitur pencarian real-time.
Jika kamu sudah familiar dengan pengelolaan data di sisi client, kamu juga bisa baca tentang Mengelola Data Lokal dengan localStorage dan sessionStorage untuk menyimpan hasil fetch secara lokal dan mengurangi request berulang.
Troubleshooting: Error yang Sering Muncul
CORS Error: “Access to fetch at ’…’ has been blocked by CORS policy”
Penyebab: Server API tidak mengizinkan request dari domain/port yang berbeda. Ini sangat sering terjadi saat kamu mengakses API pihak ketiga dari localhost.
Solusi:
// Opsi 1: Gunakan API yang mendukung CORS (contoh: JSONPlaceholder)
fetch('https://jsonplaceholder.typicode.com/posts')
// Opsi 2: Jika kamu kontrol servernya, tambahkan header CORS di backend
// Contoh di Express.js (backend):
// app.use(cors({ origin: 'http://localhost:3000' }))
// Opsi 3: Gunakan proxy di vite.config.js (untuk development)
// vite.config.js
export default {
server: {
proxy: {
'/api': 'http://localhost:5000'
}
}
}
TypeError: Cannot read properties of undefined (reading ‘map’)
Penyebab: Komponen mencoba melakukan .map() pada data yang belum selesai diambil dari API. React merender komponen sebelum data tersedia, jadi nilainya masih undefined.
Solusi:
// ❌ Salah — bisa crash jika data belum ada
const [data, setData] = useState(); // undefined!
return data.map(item => <div>{item.name}</div>);
// ✅ Benar — inisialisasi dengan array kosong
const [data, setData] = useState([]); // array kosong, aman untuk di-.map()
return data.map(item => <div key={item.id}>{item.name}</div>);
// ✅ Atau gunakan optional chaining sebagai pengaman tambahan
return data?.map(item => <div key={item.id}>{item.name}</div>);
Infinite Loop: useEffect Terus Berjalan
Penyebab: Dependency array useEffect berisi state yang diupdate di dalam useEffect itu sendiri, menyebabkan loop tak berakhir.
Solusi:
// ❌ Salah — ini akan loop selamanya!
useEffect(() => {
fetch('/api/data')
.then(res => res.json())
.then(data => setData(data)); // setData mengubah state 'data'
}, [data]); // 'data' ada di dependency → update state → trigger ulang → loop!
// ✅ Benar — gunakan array kosong jika hanya ingin fetch sekali
useEffect(() => {
fetch('/api/data')
.then(res => res.json())
.then(data => setData(data));
}, []); // Hanya berjalan sekali saat mount
Network Error / Failed to Fetch
Penyebab: URL API salah, server sedang down, atau tidak ada koneksi internet.
Solusi:
useEffect(() => {
const ambilData = async () => {
try {
const response = await fetch('https://api.example.com/data');
// Cek apakah response berhasil (status 200-299)
if (!response.ok) {
throw new Error(`Server error: ${response.status} ${response.statusText}`);
}
const data = await response.json();
setData(data);
} catch (err) {
if (err.name === 'TypeError') {
setError('Tidak dapat terhubung ke server. Periksa koneksi internetmu.');
} else {
setError(err.message);
}
} finally {
setLoading(false);
}
};
ambilData();
}, []);
Pertanyaan yang Sering Diajukan
Apa perbedaan fetch bawaan browser dengan library Axios?
fetch adalah API bawaan browser yang tidak perlu instalasi tambahan. Kelemahannya adalah kamu perlu memanggil .json() secara manual dan penanganan error-nya sedikit lebih verbose. Axios adalah library eksternal yang otomatis mem-parse JSON, memiliki penanganan error yang lebih baik, dan mendukung fitur tambahan seperti interceptors. Untuk proyek pemula, fetch sudah lebih dari cukup.
Mengapa useEffect tidak boleh langsung menjadi async?
React mengharapkan useEffect mengembalikan undefined atau sebuah fungsi cleanup. Jika kamu membuat useEffect langsung async, fungsinya akan mengembalikan Promise, bukan undefined, yang bisa menyebabkan memory leak dan perilaku tak terduga. Solusinya adalah membuat fungsi async di dalam useEffect lalu memanggilnya secara terpisah.
Bagaimana cara menghindari fetch yang terjadi berkali-kali?
Gunakan dependency array [] yang kosong jika kamu hanya ingin fetch sekali saat komponen pertama kali muncul. Jika fetch perlu diulang berdasarkan parameter tertentu (misalnya ID pengguna), masukkan parameter itu ke dalam dependency array. Kamu juga bisa menggunakan teknik debouncing untuk input pencarian agar tidak fetch setiap kali pengguna mengetik satu huruf.
Apa itu “cleanup function” di useEffect dan kapan dibutuhkan?
Cleanup function adalah fungsi yang dikembalikan dari useEffect, dijalankan saat komponen unmount atau sebelum efek dijalankan ulang. Ini penting saat bekerja dengan fetch agar tidak terjadi kondisi “race condition” — misalnya komponen sudah unmount tapi fetch belum selesai dan masih mencoba update state. Contohnya:
useEffect(() => {
const controller = new AbortController();
fetch('/api/data', { signal: controller.signal })
.then(res => res.json())
.then(data => setData(data))
.catch(err => {
if (err.name !== 'AbortError') setError(err.message);
});
return () => controller.abort(); // Cleanup: batalkan fetch jika komponen unmount
}, []);
Kapan sebaiknya menggunakan library seperti React Query atau SWR?
Gunakan React Query atau SWR ketika aplikasimu mulai kompleks dan membutuhkan fitur seperti caching otomatis, refetch saat tab aktif kembali, loading state yang lebih canggih, atau pagination. Untuk proyek kecil atau saat belajar, useEffect + fetch sudah cukup dan membantu kamu memahami cara kerja di balik library-library tersebut.
Kesimpulan
Mengambil data dari API adalah fondasi penting dalam pengembangan aplikasi React modern. Dalam artikel ini kamu sudah belajar:
- Konsep dasar API, JSON, dan siklus fetching data (loading, success, error)
- Cara menggunakan
useEffectuntuk menjalankan fetch saat komponen dimuat - Pola
async/awaityang lebih bersih dibanding.then()berantai - Fetch dinamis berdasarkan input pengguna menggunakan dependency array
- Error handling yang benar agar aplikasi tidak crash
- Cleanup function untuk mencegah memory leak saat komponen unmount
Kunci utamanya sederhana: selalu kelola tiga state — loading, data, dan error — dan gunakan useEffect dengan dependency array yang tepat.
Selamat belajar dan terus berlatih! Jika ada pertanyaan atau kamu ingin eksplorasi topik lebih lanjut, jangan ragu untuk menjelajahi artikel-artikel lainnya di KamusNgoding — masih banyak ilmu seru yang menunggumu!