Last active
January 19, 2023 14:37
-
-
Save zaknesler/4e22aa1c81d1e6af8cb4be9cbfe440e8 to your computer and use it in GitHub Desktop.
Mutable form helper for Next.js + tRPC + zod
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
BaseSyntheticEvent, | |
ChangeEventHandler, | |
useEffect, | |
useState, | |
} from 'react' | |
import { ErrorType } from '@/server/trpc/trpc' | |
import { RouterInputs, RouterOutputs, trpc } from '@/utils/trpc' | |
export type Fields = Pick< | |
JSX.IntrinsicElements['input'], | |
'name' | 'value' | 'disabled' | |
> & { | |
onChange?: ChangeEventHandler | undefined | |
errors?: string[] | null | |
} | |
const useMutableForm = < | |
K extends keyof RouterInputs, | |
H extends keyof RouterInputs[K], | |
>( | |
key: K, | |
handler: H, | |
initialData: RouterInputs[K][H], | |
) => { | |
const mutation = (trpc as any)[key][handler].useMutation() | |
const [data, setData] = useState<RouterInputs[K][H]>(initialData) | |
const errors = mutation.error?.data?.fields as ErrorType<RouterInputs[K][H]> | |
const loading = mutation.isLoading as boolean | |
const reset = () => { | |
setData(initialData) | |
mutation.reset() | |
} | |
const hasDataChanged = JSON.stringify(initialData) !== JSON.stringify(data) | |
const mutate = async () => mutation.mutateAsync(data) as RouterOutputs[K][H] | |
const fields = (name: keyof RouterInputs[K][H]): Fields => ({ | |
name: String(name), | |
value: String((data as any)[name]), | |
errors: errors?.[name], | |
onChange: changeHandler as any, | |
disabled: loading, | |
}) | |
const changeHandler = (e: BaseSyntheticEvent) => { | |
setData({ | |
...(data as any), | |
[e.target.name]: ['checkbox', 'radio'].includes(e.target.type) | |
? e.target.checked | |
: ['range', 'number'].includes(e.target.type) | |
? Number(e.target.value) | |
: e.target.value, | |
}) | |
} | |
useEffect(() => { | |
if (!window || !document || !errors) return | |
const error = Object.keys(errors)[0] | |
const el = document.querySelector(`[name="${error}"]`) | |
el && (el as any).focus() | |
}, [errors]) | |
return { | |
mutate, | |
form: data, | |
errors, | |
loading, | |
fields, | |
reset, | |
setData, | |
changeHandler, | |
hasDataChanged, | |
} | |
} | |
export default useMutableForm |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment