Episode ini memperkenalkan RTK Query: membuat API client dengan createApi dan fetchBaseQuery, mendefinisikan endpoints.query, memakai hook useGetPostsQuery di komponen, serta memahami caching otomatis, deduplication request, dan status query yang lengkap.

Sampai di episode 7 kita menangani data dari API secara manual: menulis createAsyncThunk, mengelola state loading, dan memanggil dispatch di komponen. Episode 8 memperkenalkan solusi yang lebih tinggi levelnya — RTK Query. RTK Query adalah tooling data fetching dan caching yang sudah terintegrasi penuh dengan Redux Toolkit, dirancang untuk menghapus boilerplate jaringan.
Mengapa RTK Query penting? Karena kebutuhan utama aplikasi nyata bukan hanya state lokal, melainkan server state — data yang harus di-fetch, di-cache, di-update, dan disinkronkan. RTK Query mengelola siklus hidup request otomatis: hasilnya disimpan dalam cache, request yang sama tidak dikirim dua kali, dan komponen tidak perlu memikirkan status fetching secara manual.
Pusat dari RTK Query adalah createApi. Di dalamnya kita mendefinisikan reducerPath, baseQuery untuk menangani HTTP, dan endpoints yang berisi seluruh operasi data. fetchBaseQuery adalah wrapper fetch bawaan yang sudah menangani URL, headers, dan serialisasi error:
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react"
export const postsApi = createApi({
reducerPath: "postsApi",
baseQuery: fetchBaseQuery({ baseUrl: "/api" }),
endpoints: () => ({}),
})reducerPath menentukan nama kunci state di store. baseUrl: "/api" berarti request akan menuju /api/posts ketika kita memanggil endpoint di bawahnya. Fungsi endpoints diisi lewat builder yang disediakan RTK Query.
Agar hook query bekerja, hasil createApi harus didaftarkan ke store. Ada tiga hal yang perlu dipasang: reducer, middleware, dan postsApi.middleware:
import { configureStore } from "@reduxjs/toolkit"
import { setupListeners } from "@reduxjs/toolkit/query"
import { postsApi } from "../services/postsApi"
import counterReducer from "../features/counter/counterSlice"
export const store = configureStore({
reducer: {
counter: counterReducer,
[postsApi.reducerPath]: postsApi.reducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(postsApi.middleware),
})
setupListeners(store.dispatch)Middleware RTK Query menangani caching, invalidation, dan re-fetch otomatis. Fungsi setupListeners mengaktifkan fitur refetchOnFocus dan refetchOnReconnect yang berguna di aplikasi nyata.
endpoints.query menandai operasi pembacaan data. Builder menerima builder.query yang mengembalikan data dari server:
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react"
export interface Post {
id: number
title: string
body: string
}
export const postsApi = createApi({
reducerPath: "postsApi",
baseQuery: fetchBaseQuery({ baseUrl: "/api" }),
endpoints: (builder) => ({
getPosts: builder.query<Post[], void>({
query: () => "posts",
}),
getPostById: builder.query<Post, number>({
query: (id) => `posts/${id}`,
}),
}),
})getPosts akan memanggil GET /api/posts, sementara getPostById memanggil /api/posts/{id}. Generic builder.query<Post[], void> berarti hasil berupa array Post dan argumen query-nya kosong. Argumen dipakai sebagai pengidentifikasi cache: nilai yang berbeda akan disimpan dalam slot cache yang berbeda.
Untuk setiap endpoint query, RTK Query membuat hook dengan nama terprediksi:
export const { useGetPostsQuery, useGetPostByIdQuery } = postsApiPola namanya adalah use + kapitalisasi endpoint + Query. Hook inilah yang kita pakai di komponen tanpa perlu menulis dispatch atau useEffect sama sekali.
Hook query mengembalikan banyak nilai status sekaligus: data, isLoading, isError, isSuccess, error, dan refetch:
import { useGetPostsQuery } from "../services/postsApi"
export function PostList() {
const { data: posts, isLoading, isError, error, refetch } = useGetPostsQuery()
if (isLoading) return <p>Memuat data...</p>
if (isError) return <p>Terjadi kesalahan: {JSON.stringify(error)}</p>
if (isSuccess && posts) {
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
return <button onClick={refetch}>Muat ulang</button>
}Perhatikan alurnya: selama pertama kali dimuat isLoading bernilai true, kemudian menjadi isSuccess dengan data siap dipakai. refetch memaksa pengambilan ulang tanpa menghapus cache yang sudah ada.
Query dengan argumen tinggal diteruskan sebagai parameter hook. RTK Query juga mendukung polling real-time lewat opsi pollingInterval:
export function PostDetail({ id }: { id: number }) {
const { data: post } = useGetPostByIdQuery(id, {
pollingInterval: 30000,
})
return <article>{post ? post.body : "Memuat..."}</article>
}Kode di atas akan menarik detail post setiap 30 detik. Nilai id yang berubah secara otomatis memicu request baru karena menjadi kunci cache yang berbeda.
RTK Query menyimpan hasil di dalam store dengan struktur keyed by argumen. Dua komponen berbeda yang memakai useGetPostsQuery() berbagi satu entri cache — server hanya menerima satu request. Inilah deduplication request yang dijanjikan di awal.
npm ls @reduxjs/toolkit react-reduxCache tidak hidup selamanya. Data dianggap segar selama batas waktu default 60 detik setelah dimuat. RTK Query menggabungkan beberapa mekanisme re-fetch:
refetchOnMountOrArgChange: mengambil ulang saat komponen mount dengan data basi.refetchOnFocus: mengembalikan data saat tab kembali fokus.refetchOnReconnect: menyegarkan saat koneksi internet pulih.keepUnusedDataFor: durasi cache dipertahankan setelah tidak ada subscriber.Ketiganya bisa diatur global di createApi atau per-endpoint, misalnya refetchOnFocus: true bila aplikasi membutuhkan data yang selalu terkini.
Tip
Periksa tab Network di Redux DevTools: kalian akan melihat request /api/posts hanya terkirim sekali meskipun beberapa komponen memakai data yang sama. Itulah bukti deduplication bekerja.
Episode 8 menunjukkan bahwa RTK Query menghapus pekerjaan manual data fetching: createApi dan fetchBaseQuery menangani request, hook query menyediakan status lengkap, dan sistem cache mengelola siklus hidup data secara otomatis. Komponen tidak lagi memikirkan kapan harus memanggil dispatch, cukup memakai hook dan merender sesuai status.
Inti yang harus dibawa pulang:
createApi dengan fetchBaseQuery adalah fondasi seluruh RTK Query.setupListeners.query untuk membaca dan mutation untuk menulis.useGetXQuery mengembalikan data, isLoading, isError, isSuccess, dan refetch.refetchOnFocus, refetchOnReconnect, dan polling mengatur kapan data di-fetch ulang.Di episode 9 berikutnya kita belajar mutations & cache invalidation — cara mengirim data ke server memakai useXMutation, menerapkan optimistic update lewat onQueryStarted, serta menyinkronkan daftar data dengan providesTags dan invalidatesTags setelah operasi menulis.