or
or
By clicking below, you agree to our terms of service.
New to HackMD? Sign up
Syntax | Example | Reference | |
---|---|---|---|
# Header | Header | 基本排版 | |
- Unordered List |
|
||
1. Ordered List |
|
||
- [ ] Todo List |
|
||
> Blockquote | Blockquote |
||
**Bold font** | Bold font | ||
*Italics font* | Italics font | ||
~~Strikethrough~~ | |||
19^th^ | 19th | ||
H~2~O | H2O | ||
++Inserted text++ | Inserted text | ||
==Marked text== | Marked text | ||
[link text](https:// "title") | Link | ||
 | Image | ||
`Code` | Code |
在筆記中貼入程式碼 | |
```javascript var i = 0; ``` |
|
||
:smile: | ![]() |
Emoji list | |
{%youtube youtube_id %} | Externals | ||
$L^aT_eX$ | LaTeX | ||
:::info This is a alert area. ::: |
This is a alert area. |
On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?
Please give us some advice and help us improve HackMD.
Syncing
xxxxxxxxxx
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
zodResolver is used to plug the ZodFormType validation
Accessing to the form everywhere with useFormContext
Instead of passing
form
as a prop to the bottomWrap the input components into a FormProvider
Retreive the form in any inputs using useFormContext
Correctly type Zod object
Create Zod form type
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 :
Example :
useController
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 avalueModifier
.Handle array of fields with useFieldArray
If you have an array of objects in FormType like :
You want to display X inputs that match property4 shape. You will use useFieldArray like this :
And map each items to display one input per item :
paths
This is the
name
, but for each item. Paths are accessible using the item's index. TypedPath<FormType>
Example,
name
will beformProperty4.${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 toController
Example :
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) :