TTMT.ManageWebGUI/HOOKS_USAGE_GUIDE.md
2025-12-22 14:53:19 +07:00

7.4 KiB

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

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

import { useLogout } from '@/hooks/queries'

function LogoutButton() {
  const logoutMutation = useLogout()
  
  return (
    <button onClick={() => logoutMutation.mutate()}>
      Đăng xuất
    </button>
  )
}

Kiểm tra phiên

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

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

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

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

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

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

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

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

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

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

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

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

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

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:

const { data } = useQueryData({
  queryKey: ["software-version"],
  url: API_ENDPOINTS.APP_VERSION.GET_SOFTWARE,
})

Sau:

const { data } = useGetSoftwareList()

Đơn giản hơn, type-safe hơn, và dễ bảo trì hơn!