342 lines
7.4 KiB
Markdown
342 lines
7.4 KiB
Markdown
# TanStack Query Hooks Documentation
|
|
|
|
Tất cả các API đã được tách riêng thành TanStack Query hooks trong folder `src/hooks/queries/`.
|
|
|
|
## Cấu trúc
|
|
|
|
```
|
|
src/hooks/queries/
|
|
├── index.ts # Export tất cả hooks
|
|
├── useAuthQueries.ts # Auth hooks
|
|
├── useAppVersionQueries.ts # App/Software hooks
|
|
├── useDeviceCommQueries.ts # Device communication hooks
|
|
└── useCommandQueries.ts # Command hooks
|
|
```
|
|
|
|
## Cách Sử Dụng
|
|
|
|
### 1. Auth Queries (Xác thực)
|
|
|
|
#### Đăng nhập
|
|
```tsx
|
|
import { useLogin } from '@/hooks/queries'
|
|
|
|
function LoginPage() {
|
|
const loginMutation = useLogin()
|
|
|
|
const handleLogin = async () => {
|
|
try {
|
|
await loginMutation.mutateAsync({
|
|
username: 'user',
|
|
password: 'password'
|
|
})
|
|
// Tự động lưu token vào localStorage
|
|
} catch (error) {
|
|
console.error(error)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<button
|
|
onClick={handleLogin}
|
|
disabled={loginMutation.isPending}
|
|
>
|
|
{loginMutation.isPending ? 'Đang đăng nhập...' : 'Đăng nhập'}
|
|
</button>
|
|
)
|
|
}
|
|
```
|
|
|
|
#### Đăng xuất
|
|
```tsx
|
|
import { useLogout } from '@/hooks/queries'
|
|
|
|
function LogoutButton() {
|
|
const logoutMutation = useLogout()
|
|
|
|
return (
|
|
<button onClick={() => logoutMutation.mutate()}>
|
|
Đăng xuất
|
|
</button>
|
|
)
|
|
}
|
|
```
|
|
|
|
#### Kiểm tra phiên
|
|
```tsx
|
|
import { usePing } from '@/hooks/queries'
|
|
|
|
function CheckSession() {
|
|
const { data, isLoading } = usePing(token, true)
|
|
|
|
if (isLoading) return <div>Checking...</div>
|
|
return <div>Session: {data?.message}</div>
|
|
}
|
|
```
|
|
|
|
#### Thay đổi mật khẩu
|
|
```tsx
|
|
import { useChangePassword } from '@/hooks/queries'
|
|
|
|
function ChangePasswordForm() {
|
|
const changePasswordMutation = useChangePassword()
|
|
|
|
const handleSubmit = async () => {
|
|
await changePasswordMutation.mutateAsync({
|
|
currentPassword: 'old',
|
|
newPassword: 'new'
|
|
})
|
|
}
|
|
|
|
return <button onClick={handleSubmit}>Thay đổi</button>
|
|
}
|
|
```
|
|
|
|
### 2. App Version Queries (Phần mềm/Agent)
|
|
|
|
#### Lấy danh sách agent
|
|
```tsx
|
|
import { useGetAgentVersion } from '@/hooks/queries'
|
|
|
|
function AgentList() {
|
|
const { data: agents, isLoading } = useGetAgentVersion()
|
|
|
|
if (isLoading) return <div>Loading...</div>
|
|
return <div>{agents?.length} agents</div>
|
|
}
|
|
```
|
|
|
|
#### Lấy danh sách phần mềm
|
|
```tsx
|
|
import { useGetSoftwareList } from '@/hooks/queries'
|
|
|
|
function SoftwareList() {
|
|
const { data: software, isLoading } = useGetSoftwareList()
|
|
|
|
return software?.map(item => <div key={item.id}>{item.name}</div>)
|
|
}
|
|
```
|
|
|
|
#### Upload file
|
|
```tsx
|
|
import { useUploadSoftware } from '@/hooks/queries'
|
|
|
|
function UploadForm() {
|
|
const uploadMutation = useUploadSoftware()
|
|
|
|
const handleUpload = async (file: File) => {
|
|
const formData = new FormData()
|
|
formData.append('file', file)
|
|
|
|
await uploadMutation.mutateAsync({
|
|
formData,
|
|
onUploadProgress: (event) => {
|
|
const percent = (event.loaded / event.total) * 100
|
|
console.log(`Upload: ${percent}%`)
|
|
}
|
|
})
|
|
}
|
|
|
|
return <input type="file" onChange={(e) => e.target.files && handleUpload(e.target.files[0])} />
|
|
}
|
|
```
|
|
|
|
#### Quản lý blacklist
|
|
```tsx
|
|
import {
|
|
useGetBlacklist,
|
|
useAddBlacklist,
|
|
useDeleteBlacklist
|
|
} from '@/hooks/queries'
|
|
|
|
function BlacklistManager() {
|
|
const { data: blacklist } = useGetBlacklist()
|
|
const addMutation = useAddBlacklist()
|
|
const deleteMutation = useDeleteBlacklist()
|
|
|
|
const handleAdd = async () => {
|
|
await addMutation.mutateAsync({ appId: 1 })
|
|
}
|
|
|
|
const handleDelete = async (appId: number) => {
|
|
await deleteMutation.mutateAsync(appId)
|
|
}
|
|
|
|
return (
|
|
<>
|
|
{blacklist?.map(item => (
|
|
<div key={item.id}>
|
|
{item.name}
|
|
<button onClick={() => handleDelete(item.id)}>Delete</button>
|
|
</div>
|
|
))}
|
|
<button onClick={handleAdd}>Add</button>
|
|
</>
|
|
)
|
|
}
|
|
```
|
|
|
|
### 3. Device Communication Queries
|
|
|
|
#### Lấy danh sách phòng
|
|
```tsx
|
|
import { useGetRoomList } from '@/hooks/queries'
|
|
|
|
function RoomSelector() {
|
|
const { data: rooms } = useGetRoomList()
|
|
|
|
return (
|
|
<select>
|
|
{rooms?.map(room => (
|
|
<option key={room.id} value={room.id}>{room.name}</option>
|
|
))}
|
|
</select>
|
|
)
|
|
}
|
|
```
|
|
|
|
#### Lấy thiết bị trong phòng
|
|
```tsx
|
|
import { useGetDeviceFromRoom } from '@/hooks/queries'
|
|
|
|
function DeviceList({ roomName }: { roomName: string }) {
|
|
const { data: devices, isLoading } = useGetDeviceFromRoom(roomName, true)
|
|
|
|
if (isLoading) return <div>Loading devices...</div>
|
|
|
|
return devices?.map(device => (
|
|
<div key={device.id}>{device.name}</div>
|
|
))
|
|
}
|
|
```
|
|
|
|
#### Gửi lệnh
|
|
```tsx
|
|
import { useSendCommand } from '@/hooks/queries'
|
|
|
|
function CommandForm() {
|
|
const sendMutation = useSendCommand()
|
|
|
|
const handleSend = async () => {
|
|
await sendMutation.mutateAsync({
|
|
roomName: 'Room A',
|
|
data: { command: 'dir' }
|
|
})
|
|
}
|
|
|
|
return <button onClick={handleSend}>Gửi lệnh</button>
|
|
}
|
|
```
|
|
|
|
#### Cài đặt phần mềm
|
|
```tsx
|
|
import { useInstallMsi } from '@/hooks/queries'
|
|
|
|
function InstallSoftware() {
|
|
const installMutation = useInstallMsi()
|
|
|
|
const handleInstall = async () => {
|
|
await installMutation.mutateAsync({
|
|
roomName: 'Room A',
|
|
data: { msiFileId: 1 }
|
|
})
|
|
}
|
|
|
|
return <button onClick={handleInstall}>Cài đặt</button>
|
|
}
|
|
```
|
|
|
|
### 4. Command Queries
|
|
|
|
#### Lấy danh sách lệnh
|
|
```tsx
|
|
import { useGetCommandList } from '@/hooks/queries'
|
|
|
|
function CommandList() {
|
|
const { data: commands } = useGetCommandList()
|
|
|
|
return commands?.map(cmd => <div key={cmd.id}>{cmd.name}</div>)
|
|
}
|
|
```
|
|
|
|
#### Thêm lệnh
|
|
```tsx
|
|
import { useAddCommand } from '@/hooks/queries'
|
|
|
|
function AddCommandForm() {
|
|
const addMutation = useAddCommand()
|
|
|
|
const handleAdd = async () => {
|
|
await addMutation.mutateAsync({
|
|
name: 'My Command',
|
|
command: 'echo hello'
|
|
})
|
|
}
|
|
|
|
return <button onClick={handleAdd}>Add Command</button>
|
|
}
|
|
```
|
|
|
|
## Lợi ích
|
|
|
|
1. **Automatic Caching** - TanStack Query tự động cache dữ liệu
|
|
2. **Background Refetching** - Cập nhật dữ liệu trong background
|
|
3. **Stale Time Management** - Kiểm soát thời gian dữ liệu còn "fresh"
|
|
4. **Automatic Invalidation** - Tự động update dữ liệu sau mutations
|
|
5. **Deduplication** - Gộp các request giống nhau
|
|
6. **Error Handling** - Xử lý lỗi tập trung
|
|
7. **Loading States** - Tracking loading/pending/error states
|
|
|
|
## Advanced Usage
|
|
|
|
### Dependent Queries
|
|
```tsx
|
|
function DeviceDetails({ deviceId }: { deviceId: number }) {
|
|
const { data: device } = useGetDeviceFromRoom(deviceId, true)
|
|
|
|
// Chỉ fetch khi có device
|
|
const { data: status } = useGetClientFolderStatus(
|
|
device?.roomName,
|
|
!!device
|
|
)
|
|
|
|
return <div>{status?.path}</div>
|
|
}
|
|
```
|
|
|
|
### Prefetching
|
|
```tsx
|
|
import { useQueryClient } from '@tanstack/react-query'
|
|
import { useGetSoftwareList } from '@/hooks/queries'
|
|
|
|
function PrefetchOnHover() {
|
|
const queryClient = useQueryClient()
|
|
|
|
const handleMouseEnter = () => {
|
|
queryClient.prefetchQuery({
|
|
queryKey: ['app-version', 'software'],
|
|
queryFn: () => useGetSoftwareList
|
|
})
|
|
}
|
|
|
|
return <div onMouseEnter={handleMouseEnter}>Hover me</div>
|
|
}
|
|
```
|
|
|
|
## Migration từ cách cũ
|
|
|
|
**Trước:**
|
|
```tsx
|
|
const { data } = useQueryData({
|
|
queryKey: ["software-version"],
|
|
url: API_ENDPOINTS.APP_VERSION.GET_SOFTWARE,
|
|
})
|
|
```
|
|
|
|
**Sau:**
|
|
```tsx
|
|
const { data } = useGetSoftwareList()
|
|
```
|
|
|
|
Đơn giản hơn, type-safe hơn, và dễ bảo trì hơn!
|