@twotwoba/vv-cli
Version:
Easily create Vite + React/Vue3 project with TailwindCSS and other useful libraries. Also support Chrome extension.
185 lines (171 loc) • 6.1 kB
text/typescript
import {
useQuery,
useMutation,
useInfiniteQuery,
type UseQueryOptions,
type UseMutationOptions,
type QueryKey
} from "@tanstack/react-query"
import type { FetchError } from "./server-helper"
import { fetcher, type FetcherOptions } from "./fecher"
// ============================================================================
// Query Factory - 用于创建查询 hooks
// ============================================================================
type QueryOptions<TData, TParams> = Omit<
UseQueryOptions<TData, FetchError, TData, QueryKey>,
"queryKey" | "queryFn"
> & {
params?: TParams
}
/**
* 创建查询 Hook 工厂函数
*
* @template TData 响应数据类型
* @template TParams 请求参数类型
* @param endpoint API 端点
* @param baseOptions 基础配置
*
* @example
* // 定义 API
* export const useUser = createQuery<User>('/api/user')
* export const useUserById = createQuery<User, { id: string }>('/api/user')
*
* // 使用
* const { data, isLoading } = useUser()
* const { data } = useUserById({ params: { id: '123' } })
*/
export const createQuery = <
TData = unknown,
TParams extends Record<string, unknown> = Record<string, unknown>
>(
endpoint: string,
baseOptions?: FetcherOptions
) => {
return (options?: QueryOptions<TData, TParams>) => {
const { params, ...queryOptions } = options ?? {}
return useQuery<TData, FetchError>({
queryKey: params ? [endpoint, params] : [endpoint],
queryFn: () =>
fetcher<TData>(endpoint, {
...baseOptions,
method: "GET",
params: params as Record<string, unknown>
}),
...queryOptions
})
}
}
// ============================================================================
// Mutation Factory - 用于创建变更 hooks
// ============================================================================
type MutationOptions<TData, TBody> = Omit<
UseMutationOptions<TData, FetchError, TBody>,
"mutationFn"
>
/**
* 创建变更 Hook 工厂函数
*
* @template TData 响应数据类型
* @template TBody 请求体类型
* @param endpoint API 端点
* @param method HTTP 方法
* @param baseOptions 基础配置
*
* @example
* // 定义 API
* export const useCreateUser = createMutation<User, CreateUserDTO>('/api/user', 'POST')
* export const useUpdateUser = createMutation<User, UpdateUserDTO>('/api/user', 'PUT')
* export const useDeleteUser = createMutation<void, { id: string }>('/api/user', 'DELETE')
*
* // 使用
* const { mutate, mutateAsync, isPending } = useCreateUser()
* mutate({ name: 'John', email: 'john@example.com' })
*/
export const createMutation = <TData = unknown, TBody = unknown>(
endpoint: string,
method: "POST" | "PUT" | "DELETE" | "PATCH" = "POST",
baseOptions?: Omit<FetcherOptions, "method">
) => {
return (options?: MutationOptions<TData, TBody>) => {
return useMutation<TData, FetchError, TBody>({
mutationFn: (body: TBody) =>
fetcher<TData>(endpoint, {
...baseOptions,
method,
body: body as Record<string, unknown>
}),
...options
})
}
}
// ============================================================================
// Infinite Query Factory - 用于创建无限加载 hooks
// ============================================================================
interface PageParam {
current: number
pageSize?: number
}
interface InfiniteQueryOptions<TData, TParams> {
params?: TParams
pageSize?: number
enabled?: boolean
getNextPageParam?: (lastPage: TData, allPages: TData[]) => PageParam | undefined
}
/**
* 创建无限查询 Hook 工厂函数
*
* @template TData 单页数据类型
* @template TParams 额外请求参数类型
* @param endpoint API 端点
* @param baseOptions 基础配置
*
* @example
* // 定义 API(假设返回 { list: User[], total: number })
* export const useUserList = createInfiniteQuery<{ list: User[], total: number }>('/api/users')
*
* // 使用
* const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useUserList({
* params: { status: 'active' },
* pageSize: 20
* })
*/
export const createInfiniteQuery = <
TData = unknown,
TParams extends Record<string, unknown> = Record<string, unknown>
>(
endpoint: string,
baseOptions?: FetcherOptions
) => {
return (options?: InfiniteQueryOptions<TData, TParams>) => {
const { params, pageSize = 10, getNextPageParam, ...queryOptions } = options ?? {}
return useInfiniteQuery<TData, FetchError, TData, QueryKey, PageParam>({
queryKey: params ? [endpoint, "infinite", params] : [endpoint, "infinite"],
queryFn: ({ pageParam }) =>
fetcher<TData>(endpoint, {
...baseOptions,
method: "GET",
params: {
...params,
current: pageParam.current,
pageSize: pageParam.pageSize ?? pageSize
} as Record<string, unknown>
}),
initialPageParam: { current: 1, pageSize },
getNextPageParam:
getNextPageParam ??
((lastPage, allPages) => {
// 默认实现:假设返回数据有 total 字段
const page = lastPage as { total?: number; list?: unknown[] }
const loadedCount = allPages.reduce((acc, p) => {
const items = (p as { list?: unknown[] }).list
return acc + (items?.length ?? 0)
}, 0)
if (page.total && loadedCount < page.total) {
return { current: allPages.length + 1, pageSize }
}
return undefined
}),
...queryOptions
})
}
}