Skip to main content

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.