Langsung ke konten
KamusNgoding
Pemula React 7 menit baca

Cara Mengambil Data dari API di React

#react #api #fetch #axios #beginner

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 APIAxios
Bawaan browser✅ Ya❌ Perlu install
Otomatis parse JSON❌ Manual✅ Otomatis
Error handlingLebih verboseLebih 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:

  1. Loading — Data sedang diambil dari server
  2. Success — Data berhasil didapat
  3. 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:

  1. Fungsi callback — kode yang ingin dijalankan sebagai efek samping
  2. Dependency array — daftar nilai yang “diawasi”; useEffect akan 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 kosong
  • useState(true) — status loading, awalnya true karena kita langsung fetch saat komponen muncul
  • useState(null) — menyimpan pesan error jika ada
  • useEffect dengan array [] — fetch hanya dijalankan sekali saat komponen pertama kali dimuat
  • response.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: useEffect tidak bisa langsung menerima fungsi async. Itulah mengapa kita membuat fungsi ambilArtikel di dalam useEffect dan memanggilnya secara terpisah. Blok finally memastikan setLoading(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 useEffect untuk menjalankan fetch saat komponen dimuat
  • Pola async/await yang 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!

Artikel Terkait