### 1. **Pagination** di React Query
Pagination adalah teknik untuk membagi data dalam potongan-potongan kecil sehingga lebih mudah dikelola dan ditampilkan. Dalam aplikasi web, pagination biasanya digunakan untuk membatasi jumlah data yang ditampilkan pada satu halaman dan memungkinkan pengguna untuk berpindah antar halaman.
#### Contoh Pagination dengan React Query:
Misalkan kita ingin mengambil daftar users dari API dan menerapkan pagination untuk membatasi jumlah user yang ditampilkan per halaman.
```jsx
import { useState } from 'react';
import { useQuery } from 'react-query';
import axios from 'axios';
const fetchUsers = async (page) => {
const res = await axios.get(`https://jsonplaceholder.typicode.com/users?_page=${page}&_limit=5`);
return res.data;
};
function Users() {
const [page, setPage] = useState(1);
const { data, isLoading, isError, error } = useQuery(['users', page], () => fetchUsers(page), {
keepPreviousData: true, // Tetap menyimpan data halaman sebelumnya selama transisi halaman
});
if (isLoading) return <div>Loading...</div>;
if (isError) return <div>Error: {error.message}</div>;
return (
<div>
<h1>Users List (Page {page})</h1>
<ul>
{data.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
<button
onClick={() => setPage(prev => Math.max(prev - 1, 1))}
disabled={page === 1}
>
Previous
</button>
<button onClick={() => setPage(prev => prev + 1)}>
Next
</button>
</div>
);
}
```
**Penjelasan:**
- `useQuery(['users', page], ...)`: Menggunakan array sebagai query key. `page` adalah dependensi yang membuat query di-fetch ulang setiap kali halaman berubah.
- `keepPreviousData`: Fitur ini memastikan bahwa data dari halaman sebelumnya tetap ada saat pengguna beralih ke halaman baru untuk menghindari "flashing" data kosong.
### 2. **Infinite Scrolling**
Infinite scrolling adalah teknik yang sering digunakan di aplikasi modern, di mana data baru diambil secara otomatis ketika pengguna menggulir ke bawah. Hal ini membuat pengalaman pengguna lebih lancar tanpa harus menekan tombol untuk berpindah halaman.
#### Contoh Infinite Scrolling dengan React Query:
```jsx
import { useState } from 'react';
import { useQuery } from 'react-query';
import axios from 'axios';
const fetchUsers = async (page) => {
const res = await axios.get(`https://jsonplaceholder.typicode.com/users?_page=${page}&_limit=5`);
return res.data;
};
function InfiniteScrollUsers() {
const [page, setPage] = useState(1);
const { data, isLoading, isError, error, isFetchingNextPage } = useQuery(
['users', page],
() => fetchUsers(page),
{
getNextPageParam: (lastPage, pages) => lastPage.length ? pages.length + 1 : undefined,
}
);
const handleScroll = () => {
if (window.innerHeight + window.scrollY >= document.body.offsetHeight && !isFetchingNextPage) {
setPage(prev => prev + 1);
}
};
useEffect(() => {
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
if (isLoading) return <div>Loading...</div>;
if (isError) return <div>Error: {error.message}</div>;
return (
<div>
<h1>Infinite Scroll Users</h1>
<ul>
{data.pages.map((page, index) => (
<div key={index}>
{page.map(user => (
<li key={user.id}>{user.name}</li>
))}
</div>
))}
</ul>
{isFetchingNextPage && <div>Loading more...</div>}
</div>
);
}
```
**Penjelasan:**
- `useQuery`: Di sini kita menggunakan `getNextPageParam` untuk menentukan kapan halaman berikutnya harus diambil.
- `window.addEventListener('scroll')`: Event listener untuk mendeteksi ketika pengguna mencapai akhir halaman dan secara otomatis mengambil halaman berikutnya.
### 3. **Mutations (POST, PUT, DELETE Requests)**
Selain fetching, React Query juga bisa digunakan untuk melakukan perubahan data (mutations) seperti POST, PUT, atau DELETE request.
#### Contoh Mutation (POST Request):
```jsx
import { useMutation, useQueryClient } from 'react-query';
import axios from 'axios';
const addUser = async (newUser) => {
const res = await axios.post('https://jsonplaceholder.typicode.com/users', newUser);
return res.data;
};
function AddUserForm() {
const queryClient = useQueryClient();
const mutation = useMutation(addUser, {
// Optimistically update cache
onMutate: async (newUser) => {
await queryClient.cancelQueries('users');
const previousUsers = queryClient.getQueryData('users');
queryClient.setQueryData('users', (old) => [...old, newUser]);
return { previousUsers };
},
onError: (err, newUser, context) => {
queryClient.setQueryData('users', context.previousUsers); // Rollback jika terjadi error
},
onSettled: () => {
queryClient.invalidateQueries('users'); // Fetch ulang data users
},
});
const handleSubmit = async (e) => {
e.preventDefault();
const name = e.target.elements.name.value;
mutation.mutate({ name });
};
return (
<form onSubmit={handleSubmit}>
<input name="name" type="text" placeholder="New user name" />
<button type="submit">Add User</button>
</form>
);
}
**Penjelasan:**
- `useMutation`: Hook yang digunakan untuk meng-handle POST, PUT, atau DELETE request.
- `onMutate`: Dijalankan sebelum mutation, di mana kita bisa melakukan **optimistic update** (mengubah state seolah-olah request sudah berhasil).
- `onError`: Jika mutation gagal, kita bisa **rollback** ke state sebelumnya.
- `onSettled`: Setelah mutation selesai, kita bisa men-trigger refetch untuk memperbarui data.
### Kesimpulan
Dengan pemahaman ini, kamu dapat:
- Mengimplementasikan **pagination** untuk menampilkan data dalam potongan kecil.
- Membuat **infinite scrolling** untuk pengalaman pengguna yang lebih baik dalam mengelola data yang panjang.
- Menggunakan **mutations** untuk mengirim, mengedit, atau menghapus data dari server dengan React Query.
Pagination adalah teknik untuk membagi data dalam potongan-potongan kecil sehingga lebih mudah dikelola dan ditampilkan. Dalam aplikasi web, pagination biasanya digunakan untuk membatasi jumlah data yang ditampilkan pada satu halaman dan memungkinkan pengguna untuk berpindah antar halaman.
#### Contoh Pagination dengan React Query:
Misalkan kita ingin mengambil daftar users dari API dan menerapkan pagination untuk membatasi jumlah user yang ditampilkan per halaman.
```jsx
import { useState } from 'react';
import { useQuery } from 'react-query';
import axios from 'axios';
const fetchUsers = async (page) => {
const res = await axios.get(`https://jsonplaceholder.typicode.com/users?_page=${page}&_limit=5`);
return res.data;
};
function Users() {
const [page, setPage] = useState(1);
const { data, isLoading, isError, error } = useQuery(['users', page], () => fetchUsers(page), {
keepPreviousData: true, // Tetap menyimpan data halaman sebelumnya selama transisi halaman
});
if (isLoading) return <div>Loading...</div>;
if (isError) return <div>Error: {error.message}</div>;
return (
<div>
<h1>Users List (Page {page})</h1>
<ul>
{data.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
<button
onClick={() => setPage(prev => Math.max(prev - 1, 1))}
disabled={page === 1}
>
Previous
</button>
<button onClick={() => setPage(prev => prev + 1)}>
Next
</button>
</div>
);
}
```
**Penjelasan:**
- `useQuery(['users', page], ...)`: Menggunakan array sebagai query key. `page` adalah dependensi yang membuat query di-fetch ulang setiap kali halaman berubah.
- `keepPreviousData`: Fitur ini memastikan bahwa data dari halaman sebelumnya tetap ada saat pengguna beralih ke halaman baru untuk menghindari "flashing" data kosong.
### 2. **Infinite Scrolling**
Infinite scrolling adalah teknik yang sering digunakan di aplikasi modern, di mana data baru diambil secara otomatis ketika pengguna menggulir ke bawah. Hal ini membuat pengalaman pengguna lebih lancar tanpa harus menekan tombol untuk berpindah halaman.
#### Contoh Infinite Scrolling dengan React Query:
```jsx
import { useState } from 'react';
import { useQuery } from 'react-query';
import axios from 'axios';
const fetchUsers = async (page) => {
const res = await axios.get(`https://jsonplaceholder.typicode.com/users?_page=${page}&_limit=5`);
return res.data;
};
function InfiniteScrollUsers() {
const [page, setPage] = useState(1);
const { data, isLoading, isError, error, isFetchingNextPage } = useQuery(
['users', page],
() => fetchUsers(page),
{
getNextPageParam: (lastPage, pages) => lastPage.length ? pages.length + 1 : undefined,
}
);
const handleScroll = () => {
if (window.innerHeight + window.scrollY >= document.body.offsetHeight && !isFetchingNextPage) {
setPage(prev => prev + 1);
}
};
useEffect(() => {
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
if (isLoading) return <div>Loading...</div>;
if (isError) return <div>Error: {error.message}</div>;
return (
<div>
<h1>Infinite Scroll Users</h1>
<ul>
{data.pages.map((page, index) => (
<div key={index}>
{page.map(user => (
<li key={user.id}>{user.name}</li>
))}
</div>
))}
</ul>
{isFetchingNextPage && <div>Loading more...</div>}
</div>
);
}
```
**Penjelasan:**
- `useQuery`: Di sini kita menggunakan `getNextPageParam` untuk menentukan kapan halaman berikutnya harus diambil.
- `window.addEventListener('scroll')`: Event listener untuk mendeteksi ketika pengguna mencapai akhir halaman dan secara otomatis mengambil halaman berikutnya.
### 3. **Mutations (POST, PUT, DELETE Requests)**
Selain fetching, React Query juga bisa digunakan untuk melakukan perubahan data (mutations) seperti POST, PUT, atau DELETE request.
#### Contoh Mutation (POST Request):
```jsx
import { useMutation, useQueryClient } from 'react-query';
import axios from 'axios';
const addUser = async (newUser) => {
const res = await axios.post('https://jsonplaceholder.typicode.com/users', newUser);
return res.data;
};
function AddUserForm() {
const queryClient = useQueryClient();
const mutation = useMutation(addUser, {
// Optimistically update cache
onMutate: async (newUser) => {
await queryClient.cancelQueries('users');
const previousUsers = queryClient.getQueryData('users');
queryClient.setQueryData('users', (old) => [...old, newUser]);
return { previousUsers };
},
onError: (err, newUser, context) => {
queryClient.setQueryData('users', context.previousUsers); // Rollback jika terjadi error
},
onSettled: () => {
queryClient.invalidateQueries('users'); // Fetch ulang data users
},
});
const handleSubmit = async (e) => {
e.preventDefault();
const name = e.target.elements.name.value;
mutation.mutate({ name });
};
return (
<form onSubmit={handleSubmit}>
<input name="name" type="text" placeholder="New user name" />
<button type="submit">Add User</button>
</form>
);
}
**Penjelasan:**
- `useMutation`: Hook yang digunakan untuk meng-handle POST, PUT, atau DELETE request.
- `onMutate`: Dijalankan sebelum mutation, di mana kita bisa melakukan **optimistic update** (mengubah state seolah-olah request sudah berhasil).
- `onError`: Jika mutation gagal, kita bisa **rollback** ke state sebelumnya.
- `onSettled`: Setelah mutation selesai, kita bisa men-trigger refetch untuk memperbarui data.
### Kesimpulan
Dengan pemahaman ini, kamu dapat:
- Mengimplementasikan **pagination** untuk menampilkan data dalam potongan kecil.
- Membuat **infinite scrolling** untuk pengalaman pengguna yang lebih baik dalam mengelola data yang panjang.
- Menggunakan **mutations** untuk mengirim, mengedit, atau menghapus data dari server dengan React Query.