UNPKG

next-unified-query

Version:

React hooks and components for next-unified-query-core

386 lines (379 loc) 15.5 kB
import * as next_unified_query_core from 'next-unified-query-core'; import { FetchError, QueryConfig, ZodType, FetchConfig, QueryFetcher, ExtractParams, QueryObserverResult, ExtractQueryData, ApiErrorResponse, RequestConfig, MutationMethod, NextTypeFetch, z, MutationConfig, QueryClient, QueryClientOptions, QueryState } from 'next-unified-query-core'; import * as React$1 from 'react'; import React__default, { ReactNode } from 'react'; /** * 기본 UseQuery 옵션 (공통 속성) */ interface BaseUseQueryOptions<T = any> { cacheKey: readonly unknown[]; params?: Record<string, any>; schema?: ZodType; fetchConfig?: Omit<FetchConfig, "url" | "method" | "params" | "data">; enabled?: boolean; staleTime?: number; select?: (data: T) => any; selectDeps?: any[]; /** * placeholderData: fetch 전 임시 데이터 또는 이전 데이터 유지 * 값 또는 함수(prevData, prevQuery) 모두 지원 * ReactNode(JSX)도 허용 */ placeholderData?: T | React.ReactNode | ((prevData: T | React.ReactNode | undefined, prevQuery?: any) => T | React.ReactNode); /** * gcTime: 쿼리 데이터가 사용되지 않을 때(구독자가 0이 될 때) 가비지 컬렉션까지의 시간(ms) * 이는 생명주기 관리 전략으로, maxQueries(메모리 보호)와는 별개로 동작합니다. * @default 300000 (5분) */ gcTime?: number; /** * 에러 발생 시 Error Boundary로 전파할지 여부 * - boolean: true면 모든 에러를 Error Boundary로 전파 * - function: 조건부 전파 (예: (error) => error.response?.status >= 500) * @default false */ throwOnError?: boolean | ((error: FetchError) => boolean); /** * Suspense 모드 활성화 여부 * true로 설정하면 로딩 중일 때 Promise를 throw하여 React Suspense와 통합됩니다. * @default false */ suspense?: boolean; } /** * URL 기반 UseQuery 옵션 */ interface UrlBasedUseQueryOptions<T = any> extends BaseUseQueryOptions<T> { /** * API 요청 URL */ url: string; /** * queryFn이 있으면 안됨 (상호 배제) */ queryFn?: never; } /** * Custom Function 기반 UseQuery 옵션 */ interface FunctionBasedUseQueryOptions<T = any> extends BaseUseQueryOptions<T> { /** * 복잡한 요청을 위한 커스텀 쿼리 함수 * Options 방식에서는 QueryFetcher만 전달 (GET/HEAD 메서드만 허용) * 인자: fetcher (QueryFetcher 인스턴스) */ queryFn: (fetcher: QueryFetcher) => Promise<T>; /** * url이 있으면 안됨 (상호 배제) */ url?: never; } /** * UseQuery 옵션 * URL 방식 또는 Custom Function 방식 중 하나를 선택할 수 있음 */ type UseQueryOptions<T = any> = UrlBasedUseQueryOptions<T> | FunctionBasedUseQueryOptions<T>; /** * UseQuery 결과 타입 * QueryObserverResult의 alias로, 쿼리 상태와 데이터를 포함합니다. */ type UseQueryResult<TData = unknown, TError = FetchError> = QueryObserverResult<TData, TError>; /** * Schema에서 타입을 추출하는 도우미 타입 */ type InferSchemaType<T> = T extends { schema: infer S; } ? S extends ZodType ? next_unified_query_core.z.infer<S> : any : any; type UseQueryFactoryOptions<P, T> = Omit<UseQueryOptions<T>, "cacheKey" | "url" | "queryFn" | "params" | "schema" | "fetchConfig"> & (P extends void ? { params?: P; } : keyof P extends never ? { params?: P; } : { params: P; }); /** * 캐싱과 상태 관리를 제공하는 데이터 페칭 React 훅입니다. * * **환경 호환성:** * - ❌ 서버사이드: 서버 컴포넌트와 호환되지 않음 (React context 사용) * - ✅ 클라이언트사이드: React context가 있는 React 컴포넌트에서 사용 * - ⚠️ SSR: "use client" 지시어가 있는 클라이언트 컴포넌트에서만 사용 * * @example * ```typescript * // 클라이언트 컴포넌트에서만 사용 * "use client"; * import { useQuery } from 'next-unified-query/react'; * * function UserProfile({ userId }: { userId: number }) { * const { data, isLoading, error } = useQuery(api.getUser, { params: userId }); * * if (isLoading) return <div>Loading...</div>; * if (error) return <div>Error: {error.message}</div>; * return <div>Hello {data.name}</div>; * } * ``` */ declare function useQuery<T, E = FetchError>(query: QueryConfig<any, any>, options: UseQueryFactoryOptions<ExtractParams<typeof query>, T>): UseQueryResult<T, E>; declare function useQuery<Q extends QueryConfig<any, any>, E = FetchError>(query: Q, options: UseQueryFactoryOptions<ExtractParams<Q>, ExtractQueryData<Q>>): UseQueryResult<ExtractQueryData<Q>, E>; declare function useQuery<O extends UseQueryOptions<any> & { schema: ZodType; }, E = FetchError>(options: O): UseQueryResult<InferSchemaType<O>, E>; declare function useQuery<T, E = FetchError>(options: UseQueryOptions<T>): UseQueryResult<T, E>; /** * Mutation 상태 인터페이스 */ interface MutationState<TData = unknown, TError = FetchError<ApiErrorResponse>, TVariables = void> { data: TData | undefined; error: TError | null; isPending: boolean; isSuccess: boolean; isError: boolean; } /** * 기본 UseMutation 옵션 (공통 속성) * 간소화된 타입 파라미터: TVariables, TData, TError */ interface BaseUseMutationOptions<TVariables = any, TData = unknown, TError = FetchError<ApiErrorResponse>> { /** * Mutation 실행 전 호출되는 콜백 (Optimistic Update 등에 사용) * 반환값은 context로 다른 콜백에 전달됨 */ onMutate?: (variables: TVariables) => Promise<any> | any; /** * Mutation 성공 시 호출되는 콜백 */ onSuccess?: (data: TData, variables: TVariables, context: any) => Promise<void> | void; /** * Mutation 실패 시 호출되는 콜백 */ onError?: (error: TError, variables: TVariables, context: any) => Promise<void> | void; /** * Mutation 완료 시 (성공/실패 무관) 호출되는 콜백 */ onSettled?: (data: TData | undefined, error: TError | null, variables: TVariables, context: any) => Promise<void> | void; /** * Mutation 성공 시 무효화할 쿼리 키 목록 */ invalidateQueries?: string[][] | ((data: TData, variables: TVariables, context: any) => string[][]); /** * 추가 fetch 설정 (baseURL, headers, timeout 등) */ fetchConfig?: Omit<RequestConfig, "url" | "method" | "params" | "data" | "schema">; /** * 요청 데이터 검증용 Zod 스키마 (선택적) */ requestSchema?: ZodType<TVariables>; /** * 응답 데이터 검증용 Zod 스키마 (선택적) */ responseSchema?: ZodType<TData>; /** * Mutation 에러 발생 시 Error Boundary로 전파할지 여부 * - boolean: true면 모든 에러를 Error Boundary로 전파 * - function: 조건부 전파 (예: (error) => error.response?.status >= 500) * @default false */ throwOnError?: boolean | ((error: TError) => boolean); } /** * URL 기반 UseMutation 옵션 */ interface UrlBasedUseMutationOptions<TVariables = any, TData = unknown, TError = FetchError<ApiErrorResponse>> extends BaseUseMutationOptions<TVariables, TData, TError> { /** * API 요청 URL */ url: string | ((variables: TVariables) => string); /** * HTTP 메서드 (Mutation 가능한 메서드만) */ method: MutationMethod; /** * mutationFn이 있으면 안됨 (상호 배제) */ mutationFn?: never; } /** * Function 기반 UseMutation 옵션 (통일된 시그니처) */ interface FunctionBasedUseMutationOptions<TVariables = any, TData = unknown, TError = FetchError<ApiErrorResponse>> extends BaseUseMutationOptions<TVariables, TData, TError> { /** * Custom mutation function - (variables, fetcher) 패턴 사용 */ mutationFn: (variables: TVariables, fetcher: NextTypeFetch) => Promise<TData>; /** * url/method가 있으면 안됨 (상호 배제) */ url?: never; method?: never; } /** * UseMutation 옵션 * URL 방식 또는 Custom Function 방식 중 하나를 선택할 수 있음 * 순서 변경: TVariables, TData, TError (사용 빈도 순) */ type UseMutationOptions<TVariables = any, TData = unknown, TError = FetchError<ApiErrorResponse>> = UrlBasedUseMutationOptions<TVariables, TData, TError> | FunctionBasedUseMutationOptions<TVariables, TData, TError>; /** * UseMutation 결과 인터페이스 (단순화된 시그니처) */ interface UseMutationResult<TData = unknown, TError = FetchError<ApiErrorResponse>, TVariables = any> extends MutationState<TData, TError, TVariables> { mutate: (variables: TVariables, options?: { onSuccess?: (data: TData, variables: TVariables, context: any) => void; onError?: (error: TError, variables: TVariables, context: any) => void; onSettled?: (data: TData | undefined, error: TError | null, variables: TVariables, context: any) => void; }) => void; mutateAsync: (variables?: TVariables, options?: { onSuccess?: (data: TData, variables: TVariables, context: any) => void; onError?: (error: TError, variables: TVariables, context: any) => void; onSettled?: (data: TData | undefined, error: TError | null, variables: TVariables, context: any) => void; }) => Promise<TData>; reset: () => void; } /** * useMutation 오버로드 선언 * 새로운 순서: TVariables, TData, TError */ declare function useMutation<TVariables = any, TData = unknown>(options: UseMutationOptions<TVariables, TData>): UseMutationResult<TData, FetchError, TVariables>; declare function useMutation<TVariables = any, TData = unknown, TError = FetchError<ApiErrorResponse>>(options: UseMutationOptions<TVariables, TData, TError>): UseMutationResult<TData, TError, TVariables>; declare function useMutation<TVariables = any, ResponseSchema extends ZodType = ZodType, TError = FetchError<ApiErrorResponse>>(options: UseMutationOptions<TVariables, any, TError> & { responseSchema: ResponseSchema; }): UseMutationResult<z.infer<ResponseSchema>, TError, TVariables>; declare function useMutation<TVariables = any, TData = unknown, TError = FetchError<ApiErrorResponse>>(factoryConfig: MutationConfig<TVariables, TData, TError>, overrideOptions?: Partial<BaseUseMutationOptions<TVariables, TData, TError>>): UseMutationResult<TData, TError, TVariables>; declare function HydrationBoundary({ state, children, }: { state?: Record<string, QueryState>; children: ReactNode; }): React__default.JSX.Element; interface QueryClientProviderProps { /** * QueryClient 인스턴스 (선택사항) * 제공하지 않으면 자동으로 환경에 맞는 인스턴스를 생성합니다. */ client?: QueryClient; /** * QueryClient 설정 (권장) * client가 제공되지 않은 경우 이 설정으로 QueryClient를 생성합니다. * SSR과 Client 모두에서 동일한 설정이 적용됩니다. */ config?: QueryClientOptions; children: ReactNode; } declare function QueryClientProvider({ client, config, children }: QueryClientProviderProps): React__default.JSX.Element; declare function useQueryClient(): QueryClient; /** * QueryClient 설정에 접근하는 Hook * SSR 환경에서 설정을 공유하기 위해 사용됩니다. */ declare function useQueryConfig(): QueryClientOptions | undefined; /** * Error Boundary Props */ interface QueryErrorBoundaryProps { /** * Fallback UI를 렌더링하는 함수 * @param error - 발생한 에러 * @param reset - Error Boundary를 리셋하는 함수 */ fallback?: (error: Error, reset: () => void) => React$1.ReactNode; /** * 에러 발생 시 호출되는 콜백 * 에러 로깅이나 모니터링 서비스 전송에 사용 */ onError?: (error: Error, errorInfo: { componentStack: string; }) => void; /** * Error Boundary가 리셋될 때 호출되는 콜백 */ onReset?: () => void; /** * 이 키들이 변경되면 Error Boundary가 자동으로 리셋됨 */ resetKeys?: Array<string | number>; /** * Error Boundary로 감쌀 컴포넌트 */ children: React$1.ReactNode; } /** * Query Error Boundary 컴포넌트 * * React 공식 Error Boundary 패턴을 기반으로 Next Unified Query에 최적화된 Error Boundary입니다. * 하위 컴포넌트에서 발생하는 에러를 캐치하고 fallback UI를 표시합니다. * * @example * ```tsx * <QueryErrorBoundary * fallback={(error, reset) => ( * <div> * <p>Error: {error.message}</p> * <button onClick={reset}>Retry</button> * </div> * )} * onError={(error) => console.error(error)} * > * <MyComponent /> * </QueryErrorBoundary> * ``` */ /** * QueryErrorBoundaryComponent 인터페이스 * React 18/19 호환을 위한 커스텀 타입 정의 * * @important 이 인터페이스는 React 버전 간 호환성을 위해 필수적입니다. * React.FC나 다른 내장 타입을 사용하면 React 18과 19 사이의 타입 불일치로 인해 * 컴파일 에러가 발생할 수 있습니다. */ interface QueryErrorBoundaryComponent { (props: QueryErrorBoundaryProps): React$1.JSX.Element; displayName?: string; } /** * Query Error Boundary 컴포넌트 구현 * * @important React 18/19 호환성을 위해 다음과 같이 구현됨: * 1. 함수 시그니처에서 QueryErrorBoundaryProps 타입 명시 (타입 추론 유지) * 2. React.createElement 사용 (클래스 컴포넌트를 함수로 래핑) * 3. as any 캐스팅 (React 버전 간 타입 차이 해결) * 4. 반환 타입을 React.JSX.Element로 명시 (일관된 타입) * 5. QueryErrorBoundaryComponent 인터페이스로 캐스팅 (타입 안정성) */ declare const QueryErrorBoundary: QueryErrorBoundaryComponent; /** * Error Boundary를 리셋하는 함수를 반환하는 Hook * * @example * ```tsx * function MyComponent() { * const resetErrorBoundary = useErrorResetBoundary(); * * const handleRetry = () => { * // 상태 초기화 등의 작업 수행 * resetErrorBoundary(); * }; * } * ``` */ declare const useErrorResetBoundary: () => () => void; interface QueryErrorResetBoundaryProps extends Omit<QueryErrorBoundaryProps, "resetKeys" | "onReset"> { /** * Error Boundary가 리셋될 때 호출되는 콜백 */ onReset?: () => void; } /** * Error Reset Boundary 컴포넌트 * * QueryErrorBoundary를 감싸고 리셋 기능을 Context로 제공합니다. * 하위 컴포넌트에서 useErrorResetBoundary Hook을 사용하여 Error Boundary를 리셋할 수 있습니다. * * @example * ```tsx * <QueryErrorResetBoundary * fallback={(error, reset) => <ErrorFallback error={error} reset={reset} />} * > * <App /> * </QueryErrorResetBoundary> * ``` */ declare function QueryErrorResetBoundary({ children, onReset, ...errorBoundaryProps }: QueryErrorResetBoundaryProps): React__default.JSX.Element; export { HydrationBoundary, type MutationState, QueryClientProvider, QueryErrorBoundary, type QueryErrorBoundaryProps, QueryErrorResetBoundary, type QueryErrorResetBoundaryProps, type UseMutationOptions, type UseQueryOptions, type UseQueryResult, useErrorResetBoundary, useMutation, useQuery, useQueryClient, useQueryConfig };