nextjs-demo
next.js demo using react 19 rc
git clone https://9o.is/git/nextjs-demo.git
Form.tsx
(2122B)
1 import { createContext, ReactNode, useContext, useRef, useState } from "react"
2
3 type FormProps<T> = ({
4 children: ReactNode
5 ['aria-label']?: string
6 action?: (formData: FormData) => void
7 }) & ({
8 onSubmit?: (data: FormDataMap) => void
9 validator: undefined
10 } | {
11 onSubmit?: (data: T) => void
12 validator: (data: FormDataMap) => T
13 })
14
15 export type FormDataMap = {
16 [key: string]: FormDataEntryValue | undefined
17 }
18
19 type FormContextProps = {
20 formError?: string
21 }
22
23 const FormContext = createContext<FormContextProps | undefined>(undefined)
24
25 export function Form<T>({ onSubmit, validator, children, action, ...props }: FormProps<T>) {
26 const formRef = useRef<HTMLFormElement>(null)
27 const [formError, setFormError] = useState<string | undefined>()
28
29 const internalOnSubmit = (event: React.FormEvent<HTMLFormElement>) => {
30 event.preventDefault()
31
32 const formData = new FormData(formRef.current || undefined)
33 const data: FormDataMap = Object.fromEntries(formData)
34
35 if (validator) {
36 try {
37 const validData = validator(data)
38 onSubmit?.(validData)
39 action?.(formData)
40 } catch (error) {
41 setFormError((error as Error).message)
42 }
43 return
44 } else {
45 onSubmit?.(data)
46 action?.(formData)
47 }
48 }
49
50 return (
51 <FormContext.Provider value={{ formError }}>
52 <form onSubmit={internalOnSubmit} ref={formRef} {...props}>
53 {children}
54 </form>
55 </FormContext.Provider>
56 )
57 }
58
59 type FormErrorProps = {
60 children: (error: string) => ReactNode
61 }
62
63 function FormError({ children }: FormErrorProps) {
64 const { formError } = useFormContext()
65
66 return (
67 <div role="alert">
68 {formError ? children(formError) : <> </>}
69 </div>
70 )
71 }
72
73 function useFormContext() {
74 const formContext = useContext(FormContext)
75 if (!formContext) throw new Error('useFormContext() must be used within a Form')
76 return formContext
77 }
78
79 Form.Error = FormError