Sekarang mari kita bahas lebih dalam tentang penggunaan `useQuery` dan `useMutation` di React Query. Keduanya merupakan hook yang sangat powerful untuk menangani pengambilan data dan mutasi data di aplikasi React.
### 1. **useQuery** – Pengambilan Data
`useQuery` adalah hook utama yang digunakan untuk mengambil data dari API secara efisien. Hook ini mendukung caching, re-fetching otomatis, pagination, dan bahkan handling error dengan mudah.
#### Sintaks Dasar `useQuery`
```javascript
const { data, error, isLoading, isError } = useQuery(queryKey, queryFn, options);
- `queryKey`: Unik untuk setiap request. React Query menggunakan ini untuk meng-cache dan mengidentifikasi query.
- `queryFn`: Fungsi yang digunakan untuk mengambil data, seperti fetch atau axios call.
- `options`: Berbagai konfigurasi tambahan untuk caching, polling, retry, dsb.
#### Contoh Penggunaan `useQuery`
Mari kita implementasikan contoh sederhana dari `useQuery` untuk mengambil data dari API.
```jsx
import { useQuery } from 'react-query';
import axios from 'axios';
const fetchTodos = async () => {
const response = await axios.get('https://jsonplaceholder.typicode.com/todos');
return response.data;
};
function Todos() {
const { data, error, isLoading, isError } = useQuery('todos', fetchTodos);
if (isLoading) return <div>Loading...</div>;
if (isError) return <div>Error: {error.message}</div>;
return (
<div>
<h1>Todo List</h1>
<ul>
{data.map(todo => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
</div>
);
}
#### Beberapa Opsi Konfigurasi `useQuery`
- **`staleTime`**: Menentukan berapa lama data dianggap "fresh" sebelum React Query memutuskan untuk re-fetch data. Jika data masih dalam status "fresh", data dari cache akan digunakan.
```javascript
const { data } = useQuery('todos', fetchTodos, { staleTime: 10000 }); // Data fresh selama 10 detik
```
- **`cacheTime`**: Menentukan berapa lama data akan disimpan dalam cache setelah tidak digunakan lagi (misal, user berpindah halaman).
```javascript
const { data } = useQuery('todos', fetchTodos, { cacheTime: 60000 }); // Cache data selama 1 menit
```
- **`refetchOnWindowFocus`**: Mengatur apakah React Query akan melakukan re-fetch data ketika user kembali ke tab browser.
```javascript
const { data } = useQuery('todos', fetchTodos, { refetchOnWindowFocus: false });
```
- **`enabled`**: Jika `false`, query tidak akan berjalan otomatis dan hanya bisa di-trigger secara manual.
```javascript
const { data } = useQuery('todos', fetchTodos, { enabled: false });
```
#### Refetch Data
Kamu bisa secara manual memicu re-fetch data dengan memanfaatkan `refetch` dari `useQuery`.
```javascript
const { data, refetch } = useQuery('todos', fetchTodos);
return (
<div>
<h1>Todo List</h1>
<button onClick={() => refetch()}>Refetch Data</button>
<ul>
{data.map(todo => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
</div>
);
### 2. **useMutation** – Mutasi Data (POST, PUT, DELETE)
`useMutation` digunakan untuk mengelola operasi perubahan data seperti POST, PUT, DELETE. Dengan `useMutation`, kita bisa melakukan operasi yang mengubah data dan secara otomatis memperbarui cache.
#### Sintaks Dasar `useMutation`
```javascript
const mutation = useMutation(mutationFn, options);
- `mutationFn`: Fungsi yang digunakan untuk melakukan mutasi (misalnya, POST atau PUT request).
- `options`: Opsi tambahan seperti callback untuk handling success atau error, dan melakukan optimistic updates.
#### Contoh Penggunaan `useMutation`
Di bawah ini adalah contoh bagaimana kita bisa menggunakan `useMutation` untuk menambahkan user baru ke dalam daftar.
```jsx
import { useMutation, useQueryClient } from 'react-query';
import axios from 'axios';
const addUser = async (newUser) => {
const response = await axios.post('https://jsonplaceholder.typicode.com/users', newUser);
return response.data;
};
function AddUser() {
const queryClient = useQueryClient();
const mutation = useMutation(addUser, {
onSuccess: () => {
// After mutation succeeds, invalidate the users query to fetch the updated data
queryClient.invalidateQueries('users');
},
});
const handleSubmit = (e) => {
e.preventDefault();
const name = e.target.elements.name.value;
mutation.mutate({ name });
};
return (
<form onSubmit={handleSubmit}>
<input type="text" name="name" placeholder="Enter user name" />
<button type="submit">Add User</button>
{mutation.isLoading && <p>Adding user...</p>}
{mutation.isError && <p>Error: {mutation.error.message}</p>}
</form>
);
}
#### Beberapa Opsi Konfigurasi `useMutation`
- **`onSuccess`**: Callback yang dijalankan setelah mutation berhasil. Ini sering digunakan untuk invalidating query agar data yang di-fetch bisa diperbarui.
```javascript
useMutation(addUser, {
onSuccess: () => {
queryClient.invalidateQueries('users');
},
});
- **`onError`**: Callback yang dijalankan jika mutation gagal. Ini bisa digunakan untuk menampilkan pesan error atau rollback perubahan.
```javascript
useMutation(addUser, {
onError: (error) => {
console.log(error.message);
},
});
- **`onMutate`**: Callback yang dijalankan sebelum mutation. Ini sering digunakan untuk **optimistic updates**, di mana kita mengupdate UI seolah-olah request sudah berhasil, lalu melakukan rollback jika gagal.
```javascript
useMutation(addUser, {
onMutate: (newUser) => {
queryClient.setQueryData('users', (oldData) => [...oldData, newUser]);
},
onError: (err, newUser, context) => {
queryClient.setQueryData('users', context.previousData); // Rollback jika error
},
});
### Optimistic Updates dengan `useMutation`
Optimistic updates memungkinkan aplikasi untuk merespons segera tanpa menunggu server merespons. Ini memberikan pengalaman yang lebih responsif kepada pengguna. Namun, jika mutation gagal, kita harus rollback perubahan.
```jsx
const mutation = useMutation(addUser, {
onMutate: async (newUser) => {
await queryClient.cancelQueries('users'); // Membatalkan query yang sedang berjalan
const previousUsers = queryClient.getQueryData('users');
queryClient.setQueryData('users', (old) => [...old, newUser]); // Optimistic update
return { previousUsers }; // Mengembalikan data lama jika perlu rollback
},
onError: (err, newUser, context) => {
queryClient.setQueryData('users', context.previousUsers); // Rollback
},
onSettled: () => {
queryClient.invalidateQueries('users'); // Refetch data terbaru setelah mutation
},
});
### Kesimpulan
- **`useQuery`** digunakan untuk pengambilan data dengan dukungan caching, re-fetching, dan handling error yang efisien.
- **`useMutation`** digunakan untuk operasi yang mengubah data (POST, PUT, DELETE), dengan dukungan handling success/error, dan optimisasi melalui **optimistic updates**.
- React Query mempermudah pengelolaan data server dengan API sederhana dan kaya fitur, meminimalkan boilerplate yang harus ditulis secara manual.
### 1. **useQuery** – Pengambilan Data
`useQuery` adalah hook utama yang digunakan untuk mengambil data dari API secara efisien. Hook ini mendukung caching, re-fetching otomatis, pagination, dan bahkan handling error dengan mudah.
#### Sintaks Dasar `useQuery`
```javascript
const { data, error, isLoading, isError } = useQuery(queryKey, queryFn, options);
- `queryKey`: Unik untuk setiap request. React Query menggunakan ini untuk meng-cache dan mengidentifikasi query.
- `queryFn`: Fungsi yang digunakan untuk mengambil data, seperti fetch atau axios call.
- `options`: Berbagai konfigurasi tambahan untuk caching, polling, retry, dsb.
#### Contoh Penggunaan `useQuery`
Mari kita implementasikan contoh sederhana dari `useQuery` untuk mengambil data dari API.
```jsx
import { useQuery } from 'react-query';
import axios from 'axios';
const fetchTodos = async () => {
const response = await axios.get('https://jsonplaceholder.typicode.com/todos');
return response.data;
};
function Todos() {
const { data, error, isLoading, isError } = useQuery('todos', fetchTodos);
if (isLoading) return <div>Loading...</div>;
if (isError) return <div>Error: {error.message}</div>;
return (
<div>
<h1>Todo List</h1>
<ul>
{data.map(todo => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
</div>
);
}
#### Beberapa Opsi Konfigurasi `useQuery`
- **`staleTime`**: Menentukan berapa lama data dianggap "fresh" sebelum React Query memutuskan untuk re-fetch data. Jika data masih dalam status "fresh", data dari cache akan digunakan.
```javascript
const { data } = useQuery('todos', fetchTodos, { staleTime: 10000 }); // Data fresh selama 10 detik
```
- **`cacheTime`**: Menentukan berapa lama data akan disimpan dalam cache setelah tidak digunakan lagi (misal, user berpindah halaman).
```javascript
const { data } = useQuery('todos', fetchTodos, { cacheTime: 60000 }); // Cache data selama 1 menit
```
- **`refetchOnWindowFocus`**: Mengatur apakah React Query akan melakukan re-fetch data ketika user kembali ke tab browser.
```javascript
const { data } = useQuery('todos', fetchTodos, { refetchOnWindowFocus: false });
```
- **`enabled`**: Jika `false`, query tidak akan berjalan otomatis dan hanya bisa di-trigger secara manual.
```javascript
const { data } = useQuery('todos', fetchTodos, { enabled: false });
```
#### Refetch Data
Kamu bisa secara manual memicu re-fetch data dengan memanfaatkan `refetch` dari `useQuery`.
```javascript
const { data, refetch } = useQuery('todos', fetchTodos);
return (
<div>
<h1>Todo List</h1>
<button onClick={() => refetch()}>Refetch Data</button>
<ul>
{data.map(todo => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
</div>
);
### 2. **useMutation** – Mutasi Data (POST, PUT, DELETE)
`useMutation` digunakan untuk mengelola operasi perubahan data seperti POST, PUT, DELETE. Dengan `useMutation`, kita bisa melakukan operasi yang mengubah data dan secara otomatis memperbarui cache.
#### Sintaks Dasar `useMutation`
```javascript
const mutation = useMutation(mutationFn, options);
- `mutationFn`: Fungsi yang digunakan untuk melakukan mutasi (misalnya, POST atau PUT request).
- `options`: Opsi tambahan seperti callback untuk handling success atau error, dan melakukan optimistic updates.
#### Contoh Penggunaan `useMutation`
Di bawah ini adalah contoh bagaimana kita bisa menggunakan `useMutation` untuk menambahkan user baru ke dalam daftar.
```jsx
import { useMutation, useQueryClient } from 'react-query';
import axios from 'axios';
const addUser = async (newUser) => {
const response = await axios.post('https://jsonplaceholder.typicode.com/users', newUser);
return response.data;
};
function AddUser() {
const queryClient = useQueryClient();
const mutation = useMutation(addUser, {
onSuccess: () => {
// After mutation succeeds, invalidate the users query to fetch the updated data
queryClient.invalidateQueries('users');
},
});
const handleSubmit = (e) => {
e.preventDefault();
const name = e.target.elements.name.value;
mutation.mutate({ name });
};
return (
<form onSubmit={handleSubmit}>
<input type="text" name="name" placeholder="Enter user name" />
<button type="submit">Add User</button>
{mutation.isLoading && <p>Adding user...</p>}
{mutation.isError && <p>Error: {mutation.error.message}</p>}
</form>
);
}
#### Beberapa Opsi Konfigurasi `useMutation`
- **`onSuccess`**: Callback yang dijalankan setelah mutation berhasil. Ini sering digunakan untuk invalidating query agar data yang di-fetch bisa diperbarui.
```javascript
useMutation(addUser, {
onSuccess: () => {
queryClient.invalidateQueries('users');
},
});
- **`onError`**: Callback yang dijalankan jika mutation gagal. Ini bisa digunakan untuk menampilkan pesan error atau rollback perubahan.
```javascript
useMutation(addUser, {
onError: (error) => {
console.log(error.message);
},
});
- **`onMutate`**: Callback yang dijalankan sebelum mutation. Ini sering digunakan untuk **optimistic updates**, di mana kita mengupdate UI seolah-olah request sudah berhasil, lalu melakukan rollback jika gagal.
```javascript
useMutation(addUser, {
onMutate: (newUser) => {
queryClient.setQueryData('users', (oldData) => [...oldData, newUser]);
},
onError: (err, newUser, context) => {
queryClient.setQueryData('users', context.previousData); // Rollback jika error
},
});
### Optimistic Updates dengan `useMutation`
Optimistic updates memungkinkan aplikasi untuk merespons segera tanpa menunggu server merespons. Ini memberikan pengalaman yang lebih responsif kepada pengguna. Namun, jika mutation gagal, kita harus rollback perubahan.
```jsx
const mutation = useMutation(addUser, {
onMutate: async (newUser) => {
await queryClient.cancelQueries('users'); // Membatalkan query yang sedang berjalan
const previousUsers = queryClient.getQueryData('users');
queryClient.setQueryData('users', (old) => [...old, newUser]); // Optimistic update
return { previousUsers }; // Mengembalikan data lama jika perlu rollback
},
onError: (err, newUser, context) => {
queryClient.setQueryData('users', context.previousUsers); // Rollback
},
onSettled: () => {
queryClient.invalidateQueries('users'); // Refetch data terbaru setelah mutation
},
});
### Kesimpulan
- **`useQuery`** digunakan untuk pengambilan data dengan dukungan caching, re-fetching, dan handling error yang efisien.
- **`useMutation`** digunakan untuk operasi yang mengubah data (POST, PUT, DELETE), dengan dukungan handling success/error, dan optimisasi melalui **optimistic updates**.
- React Query mempermudah pengelolaan data server dengan API sederhana dan kaya fitur, meminimalkan boilerplate yang harus ditulis secara manual.