Pada sebuah website, umumnya perlu untuk mendapatkan suatu data dari server yang kemudian nantinya akan ditampilkan pada halaman web, daftar produk misalnya. Data yang berasal dari server bisa kita sebut sebagai server state. Di React, biasanya kita menggunakan axios untuk melakukan request ke server untuk mendapatkan data dalam bentuk response dan kemudian data yang kita dapatkan tadi akan kita simpan ke state sebagai berikut.
const ProductListPage = () => {
const [products, setProducts] = useState([])
const [isLoading, setIsLoading] = useState(false)
useEffect(() => {
const fetchProducts = async () => {
try {
setIsLoading(true)
const response = await axios.get('/products')
setProducts(response)
} catch (error) {
console.log(error)
} finally {
setIsLoading(false)
}
}
fetchProducts()
}, [])
return (
<div>
{!isLoading && (
// Tampilkan daftar produk disini.
)}
{isLoading && (
// Tampilkan loading disini.
)}
</div>
)
}
Kode diatas digunakan untuk mendapatkan semua daftar produk yang kemudian untuk ditampilkan. Namun ada beberapa kasus lain terkait fetching data yang perlu diperhatikan.
- Bagaimana jika data produk terlalu banyak, ada 100,000 item misalnya? Kita bisa menerapkan pagination pada data produk dengan menampilkan 20 item per halaman.
- Apa yang harus dilakukan jika terjadi error ketika melakukan fetching data produk? Kita bisa menampilkan error pada bagian daftar produk di halaman web tersebut.
- Bagaimana cara kita bisa pulih dari error tersebut? Kita bisa mencoba melakukan fetching data ulang jika error di server sudah diperbaiki.
- Bagaimana cara kita bisa melakukan fetching data produk dari halaman A untuk kemudian ditampilkan di halaman B? Kita bisa coba fetching data produk dari halaman A kemudian hasil response akan kita simpan ke global state, dan kemudian kita tampikan di halaman B.
- Bagaimana cara kita bisa mendapatkan data terbaru dari server? Kita dapat melakukan fetching data ulang ketika dibutuhkan dari halaman web manapun.
Itu adalah sebagian dari sekian banyak kasus yang bisa terjadi terkait dengan fetching data. Untuk menerapkan hal tersebut pasti tidak mudah dan diperlukan banyak effort. Kita perlu memikirkan dimana state akan didefinisikan, memikirkan bagaimana data dari pagination akan disimpan, dan memikirkan hal-hal yang merepotkan lainnya. Belum lagi jika dalam satu website, ada banyak macam data yang harus diminta ke server untuk ditampilkan bukan hanya data terkait daftar produk saja. Benar-benar merepotkan bukan.
Solusi
Dengan menggunakan React Query, kita dapat melakukan fetching data dengan lebih baik dan menyelesaikan masalah-masalah diatas. Berikut adalah contoh sederhana untuk melakukan fetching data dengan menggunakan React Query.
Pertama kita buat sebuah custom hook di sebuah file, misalnya use-products-query.js. Disini ['products', payload] adalah sebuah query key, semacam identitas unik untuk sebuah query, jadi jika query key berubah maka query akan melakukan fetching data dan ketika data berhasil di-fetch maka isLoading akan berubah dari true menjadi false. Dan jika query dari suatu query key sudah pernah melakukan fetching data atau sudah memiliki initial data, maka isLoading akan selalu false karena React Query selanjutnya akan mencoba melakukan fetching data di belakang layar untuk query key tersebut. Untuk itu kalian bisa menggunakan isFetching.
const action = (payload) => {
const response = await axios.get('/products', {
params: payload
})
return response
}
const useProductsQuery = (payload) => {
return useQuery(['products', payload], () => action(payload))
}
Kemudian kita gunakan custom hook yang udah kita buat tadi pada sebuah entah itu halaman ataupun komponen manapun yang membutuhkannya.
const ProductListPage = () => {
const productsQuery = useProductsQuery()
return (
<div>
{productsQuery.isSuccess && (
// Tampilkan daftar produk disini.
)}
{productsQuery.isError && (
// Tampilkan error disini.
)}
{productsQuery.isLoading && (
// Tampilkan loading disini.
)}
</div>
)
}
Bisa juga untuk menerapkan filter pada data produk yang akan ditampilkan. Jadi setiap kali kategori diubah, maka akan melakukan fetching data untuk mem-filter data produk.
const ProductListPage = () => {
const [category, setCategory] = useState("gadget");
const productsQuery = useProductsQuery({ category });
// Kode lainnya disini.
};
Misalnya terjadi error sewaktu fetching data dilakukan, maka akan menampilkan pesan error dan juga sebuah tombol yang dapat digunakan untuk pulih dari error ketikan error pada server sudah diperbaiki.Jadi ketika tombol tersebut diklik, maka akan melakukan fetching data ulang dan menampilkan loading.
const ProductListPage = () => {
// Kode lainnya disini.
return (
<div>
{productsQuery.isSuccess && (
// Tampilkan daftar produk disini.
)}
{productsQuery.isError && (
<div>
<p>Produk tidak dapat ditampilkan.</p>
<button
type="button"
onClick={() => productsQuery.refetch()}
>
Coba Lagi
</button>
</div>
)}
{productsQuery.isLoading && (
// Tampilkan loading disini.
)}
</div>
)
}
Bisa juga ketika kita perlu untuk melakukan fetching data untuk halaman B dari halaman A misalnya. Halaman B akan menampilkan daftar produk yang sudah di-fetch pada halaman A (cached data), namun halaman B tetap akan melakukan fetching daftar produk di belakang layar untuk memastikan data yang didapatkan adalah data terbaru. Disini loading bisa jadi tidak ditampilkan pada halaman B karena daftar produk sudah di-fetch di halaman A.
const APage = () => {
const productsQuery = useProductsQuery()
return (
<div>
<button
type="button"
onClick={() => productsQuery.refetch()}
>
Fetch daftar produk.
</button>
</div>
)
}
const BPage = () => {
const productsQuery = useProductsQuery()
return (
<div>
{productsQuery.isSuccess && (
// Tampilkan daftar produk disini.
)}
{productsQuery.isError && (
// Tampilkan error disini.
)}
{productsQuery.isLoading && (
// Tampilkan loading disini.
)}
</div>
)
}
Kita juga dapat menerapkan pagination dengan mudah. Sebelumnya, pastikan REST API /products untuk mendukung pagination juga. Kode dibawah akan mengambil daftar produk dengan kategori gadget pada halaman 1.
const ProductListPage = () => {
const [category, setCategory] = useState("gadget");
const [page, setPage] = useState(1);
const productsQuery = useProductsQuery({ category, page });
return (
<div>
{productsQuery.isSuccess && (
<>
{/* Tampilkan daftar produk disini. */}
<Pagination
currentPage={page}
totalPages={productsQuery.data.last_page}
onPageChange={(pageIndex) => {
setPage(pageIndex)
}}
/>
</>
)}
{productsQuery.isError && (
// Tampilkan error disini.
)}
{productsQuery.isLoading && (
// Tampilkan loading disini.
)}
</div>
)
};
Kesimpulan
Dengan adanya React Query, implementasi terkait fetching data menjadi lebih mudah, lebih minim effort, kodingan lebih rapi dan mudah dibaca, dan pastinya bakal membuat kita menjadi lebih produktif. Memang diawal terasa menyulitkan, tapi percayalah, React Query sangat berguna dan layak kalian pertimbangkan untuk dipelajari.