FRONTEND REACT DATA-FETCHING STATE-MANAGEMENT WEB-PERFORMANCE USER-EXPERIENCE CACHING JAVASCRIPT TYPESCRIPT WEB-DEVELOPMENT OPTIMASI DEVELOPER-EXPERIENCE SWR REACT-QUERY TANSTACK-QUERY

Optimasi Data Fetching di Frontend: Menggali Lebih Dalam React Query (TanStack Query) dan SWR

⏱️ 15 menit baca
👨‍💻

Optimasi Data Fetching di Frontend: Menggali Lebih Dalam React Query (TanStack Query) dan SWR

Pernahkah Anda merasa frustrasi dengan kerumitan mengelola data di aplikasi web? Mulai dari menampilkan status loading, menangani error, memastikan data selalu up-to-date, hingga mencegah request yang berlebihan. Jika ya, Anda tidak sendirian. Data fetching adalah salah satu tantangan terbesar dalam pengembangan frontend modern.

Di artikel ini, kita akan menyelami dunia React Query (sekarang TanStack Query) dan SWR, dua library revolusioner yang mengubah cara kita berpikir tentang data fetching. Mereka bukan hanya sekadar alat untuk mengambil data, tapi juga solusi komprehensif untuk caching, refetching, synchronization, dan state management data server Anda. Mari kita mulai!

1. Pendahuluan: Mengapa Data Fetching Butuh Solusi Khusus?

Dalam aplikasi web modern, data adalah jantungnya. Hampir setiap interaksi melibatkan pengambilan data dari backend, menampilkannya, dan mungkin memodifikasinya. Secara tradisional, kita sering menggunakan useEffect dan useState di React untuk melakukan ini:

import React, { useState, useEffect } from 'react';

function MyComponent() {
  const [data, setData] = useState(null);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        setIsLoading(true);
        const response = await fetch('/api/posts');
        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`);
        }
        const result = await response.json();
        setData(result);
      } catch (err) {
        setError(err);
      } finally {
        setIsLoading(false);
      }
    };

    fetchData();
  }, []); // Dependensi kosong, hanya fetch sekali saat mount

  if (isLoading) return <p>Memuat data...</p>;
  if (error) return <p>Terjadi kesalahan: {error.message}</p>;

  return (
    <div>
      {/* Tampilkan data */}
    </div>
  );
}

Kode di atas terlihat sederhana, bukan? Tapi ini baru permulaan. Bayangkan jika Anda perlu:

Semua ini akan menambah kompleksitas yang signifikan pada boilerplate kode Anda. Di sinilah React Query dan SWR hadir sebagai penyelamat. Mereka menyediakan abstraksi yang kuat untuk mengelola server state, memungkinkan Anda fokus pada logika bisnis, bukan pada detail data fetching yang melelahkan.

2. Memahami Perbedaan Fundamental: UI State vs. Server State

Sebelum kita melangkah lebih jauh, penting untuk memahami perbedaan antara UI State dan Server State:

React Query dan SWR dirancang khusus untuk mengelola kompleksitas Server State ini. Mereka membawa pola-pola terbaik dari data fetching ke frontend Anda.

3. React Query (TanStack Query): Toolkit Lengkap untuk Server State

React Query, yang kini menjadi bagian dari ekosistem TanStack Query, adalah library yang sangat powerful dan fleksibel. Ia menyediakan “hooks” yang intuitif untuk mengelola server state Anda dengan cara yang efisien dan declarative.

📌 Konsep Inti React Query:

Contoh Penggunaan useQuery

Mari kita ubah contoh MyComponent sebelumnya menggunakan React Query:

import React from 'react';
import { useQuery, QueryClient, QueryClientProvider } from '@tanstack/react-query'; // Atau '@tanstack/react-query' untuk versi terbaru

// Buat instance QueryClient
const queryClient = new QueryClient();

// Fungsi untuk fetching data
const fetchPosts = async () => {
  const response = await fetch('/api/posts');
  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }
  return response.json();
};

function MyComponent() {
  const { data, isLoading, isError, error } = useQuery({
    queryKey: ['posts'], // Kunci unik untuk cache data ini
    queryFn: fetchPosts, // Fungsi yang akan melakukan fetching
    staleTime: 5 * 60 * 1000, // Data dianggap fresh selama 5 menit
    cacheTime: 10 * 60 * 1000, // Data akan tetap di cache selama 10 menit setelah tidak digunakan
  });

  if (isLoading) return <p>Memuat data...</p>;
  if (isError) return <p>Terjadi kesalahan: {error.message}</p>;

  return (
    <div>
      <h1>Daftar Postingan</h1>
      {data.map(post => (
        <div key={post.id}>
          <h2>{post.title}</h2>
          <p>{post.body}</p>
        </div>
      ))}
    </div>
  );
}

// Aplikasi Anda harus dibungkus dengan QueryClientProvider
function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <MyComponent />
    </QueryClientProvider>
  );
}

export default App;

💡 Penjelasan Konsep:

Manfaat langsung:

4. SWR: Simplicity with Stale-While-Revalidate

SWR (Stale-While-Revalidate) adalah library lain yang populer, dikembangkan oleh tim Vercel (pembuat Next.js). Namanya berasal dari strategi HTTP caching stale-while-revalidate.

🎯 Konsep Inti SWR:

Ini memberikan pengalaman pengguna yang sangat cepat karena UI tidak perlu menunggu data terbaru untuk ditampilkan.

Contoh Penggunaan useSWR

Mari kita adaptasi MyComponent menggunakan SWR:

import React from 'react';
import useSWR, { SWRConfig } from 'swr';

// Fungsi fetcher untuk SWR
const fetcher = async (url) => {
  const response = await fetch(url);
  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }
  return response.json();
};

function MyComponent() {
  const { data, error, isLoading } = useSWR('/api/posts', fetcher);

  if (isLoading) return <p>Memuat data...</p>;
  if (error) return <p>Terjadi kesalahan: {error.message}</p>;

  return (
    <div>
      <h1>Daftar Postingan</h1>
      {data.map(post => (
        <div key={post.id}>
          <h2>{post.title}</h2>
          <p>{post.body}</p>
        </div>
      ))}
    </div>
  );
}

// Aplikasi Anda bisa dibungkus dengan SWRConfig untuk konfigurasi global
function App() {
  return (
    <SWRConfig value={{
      refreshInterval: 3000, // Otomatis refetch setiap 3 detik
      revalidateOnFocus: true, // Refetch saat window focus
    }}>
      <MyComponent />
    </SWRConfig>
  );
}

export default App;

💡 Penjelasan Konsep:

Manfaat langsung:

5. React Query vs. SWR: Kapan Memilih yang Mana?

Meskipun keduanya bertujuan untuk memecahkan masalah yang sama, ada beberapa perbedaan yang bisa menjadi pertimbangan:

Fitur/AspekReact Query (TanStack Query)SWR
Fokus UtamaToolkit lengkap untuk server state, lebih banyak fitur.Simplicity, pola stale-while-revalidate.
APILebih opinionated, banyak opsi konfigurasi per query.Sangat minimalis, mudah dipelajari.
Optimistic UpdatesSangat powerful dan terintegrasi dengan baik.Membutuhkan implementasi manual yang sedikit lebih rumit.
PrefetchingMudah dilakukan dengan queryClient.prefetchQuery().Membutuhkan trik atau workaround.
Mutations (useMutation)API yang kuat untuk POST/PUT/DELETE, dengan optimistic updates.Tidak ada API khusus, perlu penanganan manual.
DevToolsDevTools yang sangat lengkap dan informatif.DevTools lebih dasar, namun tetap membantu.
Ukuran BundleSedikit lebih besar karena fitur yang lebih banyak.Lebih ringan.
Kapan memilihProyek skala besar, butuh kontrol penuh atas cache, optimistic updates yang kompleks, mutations.Proyek kecil hingga menengah, prioritas pada kesederhanaan, fast UX dengan stale-while-revalidate.

💡 Kesimpulan umum: Jika Anda mencari solusi yang paling lengkap dan fleksibel untuk manajemen server state dengan fitur optimistic update dan mutation yang kuat, React Query adalah pilihan yang sangat baik. Jika Anda mengutamakan kesederhanaan, ukuran bundle yang kecil, dan pola stale-while-revalidate yang efektif, SWR adalah pilihan yang solid.

6. Best Practices dan Tips Praktis

Menggunakan React Query atau SWR saja sudah memberikan peningkatan signifikan, tetapi ada beberapa praktik terbaik untuk memaksimalkan potensi mereka:

📌 1. Invalidasi Cache yang Tepat

Setelah melakukan operasi POST, PUT, atau DELETE, data di cache Anda kemungkinan besar sudah tidak valid. Anda perlu memberi tahu library untuk me-refetch data terkait.

Dengan React Query (useMutation):

import { useMutation, useQueryClient } from '@tanstack/react-query';

function AddPostForm() {
  const queryClient = useQueryClient();

  const addPost = useMutation({
    mutationFn: (newPost) => fetch('/api/posts', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(newPost),
    }).then(res => res.json()),
    onSuccess: () => {
      // ✅ Invalidasi cache 'posts' setelah post baru berhasil ditambahkan
      queryClient.invalidateQueries({ queryKey: ['posts'] });
    },
  });

  // ...
}

Dengan SWR (mutate):

import useSWR, { mutate } from 'swr';

function AddPostForm() {
  // ... (logika form)

  const handleSubmit = async (newPost) => {
    await fetch('/api/posts', { /* ... */ });
    // ✅ Invalidasi cache '/api/posts'
    mutate('/api/posts');
  };

  // ...
}

💡 2. Optimistic Updates

Untuk pengalaman pengguna yang sangat responsif, gunakan optimistic updates. Ini berarti Anda meng-update UI seolah-olah request ke backend sudah berhasil, lalu me-revert jika request gagal.

React Query memiliki API yang kuat untuk ini di useMutation:

const addPost = useMutation({
  mutationFn: (newPost) => fetch('/api/posts', { /* ... */ }),
  onMutate: async (newPost) => {
    // ✅ Batalkan refetch yang sedang berjalan untuk menghindari race condition
    await queryClient.cancelQueries({ queryKey: ['posts'] });

    // ✅ Simpan data cache sebelumnya
    const previousPosts = queryClient.getQueryData(['posts']);

    // ✅ Update cache secara optimistic
    queryClient.setQueryData(['posts'], (old) => [...old, { id: Date.now(), ...newPost }]);

    return { previousPosts }; // Kembalikan context untuk onError
  },
  onError: (err, newPost, context) => {
    // ❌ Jika gagal, kembalikan cache ke kondisi sebelumnya
    queryClient.setQueryData(['posts'], context.previousPosts);
  },
  onSettled: () => {
    // ✅ Pastikan data terbaru diambil setelah mutation selesai
    queryClient.invalidateQueries({ queryKey: ['posts'] });
  },
});

✅ 3. Manfaatkan DevTools

Kedua library ini menyediakan DevTools yang sangat membantu untuk memvisualisasikan cache, queries, dan mutations Anda. Jangan ragu untuk menggunakannya selama pengembangan untuk debugging dan optimasi.

⚠️ 4. Hindari useEffect untuk Data Fetching Utama

Setelah Anda mengadopsi React Query atau SWR, sebisa mungkin hindari pola useEffect + useState untuk data fetching utama. Biarkan library ini yang mengelola server state Anda. Gunakan useEffect hanya untuk side effects yang tidak berhubungan dengan data fetching atau synchronization server.

Kesimpulan

Mengelola data di aplikasi frontend modern adalah tugas yang kompleks. Dengan React Query (TanStack Query) dan SWR, Anda dapat mengubah boilerplate kode yang membosankan dan rentan kesalahan menjadi logic data fetching yang bersih, efisien, dan tangguh. Mereka mengurus caching, refetching, synchronization, error handling, dan banyak lagi, memungkinkan Anda fokus pada fitur-fitur yang benar-benar penting bagi pengguna Anda.

Memilih antara keduanya tergantung pada kebutuhan proyek Anda. Jika Anda menginginkan solusi yang sangat lengkap dan fleksibel, React Query adalah pilihan yang tepat. Jika Anda mencari kesederhanaan dan kecepatan dengan pola stale-while-revalidate, SWR akan melayani Anda dengan baik. Apapun pilihan Anda, pastikan untuk mengadopsi praktik terbaik untuk mendapatkan hasil maksimal dari library ini dan memberikan pengalaman pengguna yang luar biasa. Selamat mencoba!

🔗 Baca Juga