Form
General content
We use react-hook-form
and zod
to create the form. Here are the basic steps to create a simple form
1. Use zod
to create form field schema
You can set error message with i18n, you can read this file to learn about i18n
import { z as zod } from 'zod'
export function ComponentForm() {
const t = useComponentI18N() // Set error message with i18n.
const schema = zod.object({
name: zod.string(), // string
age: zod.number(t.needBeNumber()).positive(t.needGreaterThanZero()), // > 0
country: zod.string(t.countyNeedBeString()).optional(), // string | undefined
address: zod
.string()
.min(1)
.refine((address) => ValidAddress(address), t.InvalidAddress), // You can use other methods to validate this field
})
// ...
}
2. Call useForm
to get the method collection
import { zodResolver } from '@hookform/resolvers/zod'
import { useForm } from 'react-hook-form'
const methods = useForm<formType>({
resolver: zodResolver(schema),
defaultValues: {
name: '',
age: 1,
address: '',
},
})
react-hook-form
provides optional arguments, you can change it on demand.
3. Create form UI with Controller
and Material-UI component
The react-hook-form
provides the Controller
component without import other packages to support UI libraries
const {
control,
handleSubmit,
formState: { errors, dirtyFields, isDirty },
} = useForm(options)
const onSubmit = handleSubmit((data) => doSomething())
return (
<form>
<Controller
render={({ field }) => <TextField {...field} helperText={errors.name?.message} error={dirtyFields.name} />}
name="name"
/>
<Button onClick={onSubmit} disabled={!isValid} />
</form>
)
Caveats
Use useFormContext
to get methods in children component
In practice, you may need to get form methods in the children component. You can use useFormContext
and FormProvider
to resolve this problem.
// Parent component
const methods = useForm()
return <FormProvider {...methods}>....</FormProvider>
// Children component
const { control, register, formState } = useFormContext()
Set field
Sometimes we need set some field from remote data. You can use setValue
to change these field. If you want to trigger valid while setting the field, you can add the shouldValid
option
const { watch, setValue } = useForm()
// You can use watch to monitor some field change
const address = watch('address')
useEffect(() => {
const { symbol } = fetchDataByAddress(address)
setValue('symbol', symbol, { shouldValid: true })
}, [address])
Be careful with watch
Sometimes we need to listen to the field update to do something. Although you can use watch
to react to a field. But it will cause extra renders and cause a potential performance problem. Try to use getValues
if that suits you.