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.