Files
aerbim-ht-monitor/frontend/app/hooks/useClientFetch.ts
2025-08-29 14:01:39 +03:00

132 lines
3.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useQuery, useMutation, UseQueryOptions, UseMutationOptions } from '@tanstack/react-query'
import axios, { AxiosError, AxiosRequestConfig } from 'axios'
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
interface FetchOptions<TData, TVariables, TError> {
method?: HttpMethod
config?: AxiosRequestConfig
queryOptions?: Omit<UseQueryOptions<TData, TError>, 'queryKey' | 'queryFn'>
mutationOptions?: Omit<UseMutationOptions<TData, TError, TVariables>, 'mutationFn'>
}
interface QueryResult<TData, TError> {
data: TData | undefined
isLoading: boolean
error: TError | null
refetch: () => Promise<unknown>
}
interface MutationResult<TData, TVariables, TError> {
mutate: (variables: TVariables) => void
isLoading: boolean
error: TError | null
data: TData | undefined
}
export function useClientFetch<TData = unknown, TVariables = void, TError = AxiosError>(
url: string,
options: FetchOptions<TData, TVariables, TError> = {}
): TVariables extends void
? QueryResult<TData, TError>
: MutationResult<TData, TVariables, TError> {
const { method = 'GET', config = {}, queryOptions = {}, mutationOptions = {} } = options
const API_URL = process.env.NEXT_PUBLIC_API_URL
const fullUrl = url.startsWith('http') ? url : `${API_URL}${url}`
// всегда вызываем оба хука
const query = useQuery<TData, TError>({
queryKey: [url, config.params],
queryFn: async () => {
const response = await axios.get(fullUrl, config)
return response.data
},
...queryOptions,
// отключаем автоматическое выполнение для мутаций
enabled: method === 'GET' && queryOptions.enabled !== false,
})
const mutation = useMutation<TData, TError, TVariables>({
mutationFn: async (variables: TVariables) => {
const response = await axios({
method: method.toLowerCase(),
url: fullUrl,
data: variables,
...config,
})
return response.data
},
...mutationOptions,
})
// возвращаем соответствующий результат в зависимости от метода
if (method === 'GET') {
return {
data: query.data,
isLoading: query.isLoading,
error: query.error,
refetch: query.refetch,
} as TVariables extends void ? QueryResult<TData, TError> : never
}
return {
mutate: mutation.mutate,
isLoading: mutation.isPending,
error: mutation.error,
data: mutation.data,
} as TVariables extends void ? never : MutationResult<TData, TVariables, TError>
}
// примеры использования:
/*
// GET запрос
interface UserData {
id: number
name: string
email: string
}
const { data, isLoading, error } = useClientFetch<UserData>('/users/me')
// POST запрос с типизированным payload
interface LoginPayload {
email: string
password: string
}
interface LoginResponse {
token: string
user: UserData
}
const { mutate, isLoading } = useClientFetch<LoginResponse, LoginPayload>('/auth/login', {
method: 'POST',
mutationOptions: {
onSuccess: (data) => {
// data типизирован как LoginResponse
},
onError: (error) => {
// error типизирован как AxiosError
}
}
})
// использование:
mutate({ email: 'user@example.com', password: '123456' })
// PATCH запрос
interface UpdateUserPayload {
name?: string
email?: string
}
const { mutate } = useClientFetch<UserData, UpdateUserPayload>('/users/me', {
method: 'PATCH'
})
// использование:
mutate({ name: 'New Name' })
*/