# react-hook-form + Zod : Minimal input components and strong typing
I tried to find a good pattern to make efficients forms with fields control, strongly typed and minial TSX.
Actually, we have many big input components with many props and callbacks. Some use react-hook-form, some not. We have to navigate into multiple places to modify each input and give to them specific logics.
The goal of this reflexion is to harmonize and facilitate our forms.*
*Instead of using specific logics on each input component, why no just validate fields through the form type ?*
*react-hook-form allows to do that using`resolver` in association with a typing library (Zod in our case). Thanks to that, we centralise the form validation in a Zod type.
We use `zodResolver` from `@hookform/resolvers/zod`
___
*I hope it's approximately clear and it's a good usage of this pattern, it's a ok solution for our needs, maybe we could have another approach for our forms.
But anyway, a refacto is needed, or, at least, a documented standard
This document is certainly incomplete*
___
## Create and use forms
### useForm
````
const form = useForm<FormType>({
mode: "all",
resolver: zodResolver(ZodFormType),
});
````
zodResolver is used to plug the ZodFormType validation
### Accessing to the form everywhere with useFormContext
Instead of passing `form` as a prop to the bottom
#### Wrap the input components into a FormProvider
```
<FormProvider {...form}>
...inputs, form usage
</FormProvider>
```
#### Retreive the form in any inputs using useFormContext
```
const collectionForm = useFormContext<FormType>();
```
## Correctly type Zod object
### Create Zod form type
```
export const ZodFormtype = z.object({
...shape
})
```
### Declare fields
#### Optional fields
Use `optional()`
#### Mandatory fields
Use `.min(1)`
#### Prevent blank value in fields
Use `trim()`
#### Refined validation
Use `refine()`. It allows to use more versatile validation than just Typescript representation (Using expression)
#### Optional field with no error if no value
In the case we want to validate the input value and show an error only if there is a value :
- `property1: z.string().trim().refine(!value || value => EAIL_REGEXP.test(value)).optional()`
#### Custom error messages
In most of Zod methods, you can provide a second arguent as error message
Examples :
- `property2: z.string().trim().min(1, "This field is required")`
- `property3: z.string().trim().min(1, "This field is required").refine(value => URL_REGEXP.test(value),
"Only URLs are allowed")`
## Minimal usage of TextInput (string value)
It's recommended to make a component with these properies :
- Generic type
- name
- form
- placeholder
- valueModifier
- props for style, ...
Example :
```
<TextInput
placeholder={placeHolder}
onChangeText={(text) =>
valueModifier ?
field.onChange(valueModifier(text)) :
field.onChange(text)}
value={field.value || ""}
/>
```
### useController
```
const { fieldState, field } = useController<FormType>({
name,
control: form.control,
});
```
#### name
It must match the field name from the FormType declaration
#### control
Used to plug the form's values to the field
#### value
Set the TextInput as controlled component. We set the `value` prop to pass a default value `""`.
Prevents this error : `A component is changing a controlled input to be uncontrolled`
#### valueModifier
Used to force the front value. Example : Force the value to be uppercase. We can use `toUppercase()` in the ZodFormType, but it will not modify the front value in the form. So, we can use a `valueModifier`.
### Handle array of fields with useFieldArray
If you have an array of objects in FormType like :
```
property4: {
label: string
description: string
}[]
```
You want to display X inputs that match property4 shape. You will use useFieldArray like this :
```
const { fields, update, append, remove } = useFieldArray({
control: form.control,
name: "property4",
});
```
And map each items to display one input per item :
```
{fields.map((item, index) =>
...TextInput usage with FormType and name (path) as explained above
)}
```
#### paths
This is the `name`, but for each item. Paths are accessible using the item's index. Typed `Path<FormType>`
Example, `name` will be
`formProperty4.${itemIndex}.fieldProperty1`
#### fields
All the property4 values
#### update
Used to change the property4 value (At itemIndex)
#### append
Used to add a property4 item
#### remove
Used to remove a property4 item (From itemIndex)
## Usage of another inputs with <Controller/>
You can plug a TextInput to the form using `useController`.
But you could want to plug another component as input to the form.
Example : A DateTimeInput.
To do that, you will wrap the component into `Controller`, and pass the form control, the FormType and the field name to `Controller`
Example :
```
<Controller<FormType>
control={form.control}
name="property5"
render={({ field: { onChange } }) =>
<DateTimeInput
onSelectDateTime={dateTime => onChange(dateTime)}
/>
)}
/>
```
### onChange
Used to set the field's value
### value
The current field's value
## Display field errors
Simply add this near each input component to show the field's errors (Default errors or custom errors specified in ZodFormType) :
```
<ErrorText>
{form.getFieldState("property6").error?.message}
</ErrorText>
```