83 lines
2.6 KiB
TypeScript
83 lines
2.6 KiB
TypeScript
"use client"
|
|
|
|
import { useEffect } from "react"
|
|
import { useForm, type DefaultValues, type FieldValues, type UseFormReturn } from "react-hook-form"
|
|
import { zodResolver } from "@hookform/resolvers/zod"
|
|
import { useQuery, useQueryClient, type QueryKey } from "@tanstack/react-query"
|
|
import type { ZodType } from "zod"
|
|
|
|
type UseResourceFormOptions<TFormValues extends FieldValues, TApiData = unknown> = {
|
|
schema: ZodType<TFormValues, any, any>
|
|
defaultValues: DefaultValues<TFormValues>
|
|
resourceId?: string | null
|
|
initialize?: (id: string) => Promise<TApiData>
|
|
mapToFormValues: (data: TApiData) => TFormValues
|
|
initialData?: TApiData | null
|
|
queryKey?: QueryKey
|
|
}
|
|
|
|
type UseResourceFormReturn<TFormValues extends FieldValues, TApiData = unknown> = {
|
|
form: UseFormReturn<TFormValues>
|
|
isEditing: boolean
|
|
isInitializing: boolean
|
|
invalidate: () => void
|
|
data: TApiData | undefined
|
|
}
|
|
|
|
export function useResourceForm<TFormValues extends FieldValues, TApiData = unknown>({
|
|
schema,
|
|
defaultValues,
|
|
resourceId,
|
|
initialize,
|
|
mapToFormValues,
|
|
initialData,
|
|
queryKey,
|
|
}: UseResourceFormOptions<TFormValues, TApiData>): UseResourceFormReturn<TFormValues, TApiData> {
|
|
const isEditing = !!resourceId
|
|
const queryClient = useQueryClient()
|
|
const resolvedQueryKey = queryKey ?? ["resource", resourceId]
|
|
|
|
const { data: queriedData, isLoading: isQueryLoading } = useQuery<TApiData>({
|
|
queryKey: resolvedQueryKey,
|
|
queryFn: () => initialize!(resourceId!),
|
|
enabled: isEditing && !!initialize,
|
|
staleTime: 0,
|
|
refetchOnMount: "always",
|
|
})
|
|
|
|
const resolvedData: TApiData | undefined =
|
|
queriedData ?? (isEditing ? (initialData ?? undefined) : undefined)
|
|
|
|
const form = useForm<TFormValues>({
|
|
resolver: zodResolver(schema) as any,
|
|
defaultValues,
|
|
})
|
|
|
|
useEffect(() => {
|
|
if (!isEditing) {
|
|
if (initialData) {
|
|
form.reset({ ...defaultValues, ...mapToFormValues(initialData) } as any)
|
|
} else {
|
|
form.reset(defaultValues)
|
|
}
|
|
return
|
|
}
|
|
|
|
if (resolvedData) {
|
|
form.reset(mapToFormValues(resolvedData) as any)
|
|
}
|
|
}, [isEditing, resolvedData, initialData]) // eslint-disable-line react-hooks/exhaustive-deps
|
|
|
|
const invalidate = () => {
|
|
queryClient.invalidateQueries({ queryKey: resolvedQueryKey })
|
|
}
|
|
|
|
return {
|
|
form,
|
|
isEditing,
|
|
isInitializing: isEditing && !!initialize && isQueryLoading,
|
|
invalidate,
|
|
data: resolvedData,
|
|
}
|
|
}
|