Why Frontend validation ?
Sometimes I wonder why we bother to make validation on the frontend side when there is a bunch of it on the backend side π€. Reflecting on managing several frontend projects here is the key takeaway :
1. Saving Time and Resources
By doing front end validation we could reduce the time taken by network request. Moreover, we are also saving back end resources as we reduce the number of invalid requests from the front end.
2. Better User Experience
Frontend validation could improve the user experience by providing instant feedback when they make an error. This way user could correct their errors in real-time which increases their engagement with our application.
3. Increase security and data consistency
Backend validation wasnβt bullet proof, sometimes human error happen and we forgot some validation and our data become inconsistent (such as unusual email, or phone number format). Therefore, having the 2nd layer protection was definitely better than only one layer. Moreover, front end validation decrease security issue to some extends (such as SQL injection, XSS, etc).
Why use react-hooks-form ?
The reason was quite clear from the official website βPerformant, flexible and extensible forms with easy-to-use validation. - react-hook-form.comβ π€©. personally, I used react hooks form because of:
- Isolate re-render: optimizing react rendering could be overwhelming for beginner - intermediate developers. React hooks form helps to mitigate those issue in the form, it makes sure every field only render when they are being edited.
- Consistent Validation Strategy: react hooks form provides a structured way of doing validation. Hence, every developer would use the same validation strategy.
- Developer Experience: this library provides intuitive API that could be used seamlessly across React and React Native environments.
Built-in validation in react-hooks-form
Let's dive into the code. Assume that we want to create a form for pre-ordering sandwiches. Here's the basic code:
hooks-validation-article
The code above is basic and not yet integrated with react-hook-form
. To integrate, we need to install react-hook-form
by running:
yarn add react-hook-form
Next, we need to call the useForm
hook:
const {register, handleSubmit, formState: { errors } } = useForm()
useForm
returns several functions, but for our demonstration, we will focus on the following essential ones:
-
register
: this function binds our input data touseForm
. We need to specify the inputname
in theregister
function, and optionally, we can pass validation rules. -
handleSubmit
: this function binds our form's submit behavior, it takes 2 functions as parameter:-
onSuccess
: the callback function that receives all data from registered fields. This function is triggered only when the data passes validation. -
onError
: the callback function that captures validation errors for all registered fields. This function is triggered only when validation fails.
-
-
formState.errors
: this state contains the error messages from our fields' validation.
Now, register our fields with register
function then bind our form onSubmit
with handleSubmit:
<form onSubmit={handleSubmit(
(data) => {
console.log("successfully submit", data)
},
(error) => {
console.log("some validation had failed", error)
}
)}
>
...
<label>Name</label>
<input {...register("name")} />
...
<label>Email</label>
<input {...register("email")}/>
...
<label>Phone Number</label>
<input {...register("phone-number")}/>
...
<label>Sandwich Qty</label>
<input {...register("qty")}/>
...
So far, we have integrated the react-hook-form into our project π. For the final step in this section, let's set up field validation and display an error message when a field fails validation.
to add rules
(validation) to the fields we could pass the RegisterConfiguration
object as the 2nd parameter to the register
function :
...
<div style={{ display: 'flex', width: '50%', flexDirection: 'column', }} >
<div style={{ display: 'flex', flex: 1, justifyContent: 'space-between', }} >
<label>Name</label>
<input {...register('name',
{
required: 'name is required',
minLength: {
value: 3,
message: 'name should have minimum 3 characther'
}
})} />
</div>
{errors.name && <p style={{ color: 'red' }}>{errors.name.message}</p>}
</div>
...
The code above indicates that the name
field is required and must have a minimum of 3 characters. If the validation fails, an error message will appear stating that either name is required
or name should have a minimum of 3 characters
.
the validation rules quite vary, here is the list:
-
maxLength
: validate the value of the field to have a maximum character length of x -
minLength
: validate the value of the field to have a minimum character length of x -
max
: validate the fields with number type to had max value of x -
max
: validate the fields with number type to had min value of x -
pattern
: validate the value of the field to match the Regex pattern -
validate
: validate the value of the field to pass the custom-made function
Here is the demonstration of validating email
fields with custom made function to make sure it contains @dev.to
value:
<input {...register('email',
{ required: 'email fields are required',
validate: (value) => {
if (!value.includes('@dev.to')) {
return 'need to use dev to email';
} return true;
}, })} />
the latest code that implements build-in validation of react-hook-form could be found on build-in-validate
branch
Robust validation in react-hooks-form
This method of creating validation using react-hook-form is concise and helps us organize our validation more efficiently π. However, there is a more robust approach to validation in react-hook-form that involves using resolvers
(validation resolvers). Resolvers
is an adapter that integrates react-hook-form with 3rd-party validation libraries such as class-validator
, joi
, zod
, etc. By delegating the validation to these libraries, we can have a more robust validation in our apps π₯.
Suppose we choose class-validator
as our validation library. To install class-validator
and resolver
, run the following command:
yarn add @hookform/resolvers class-transformer class-validator
Then, create a class-validator
schema:
export class SandwichPO {
@MinLength(5)
name: string;
@IsEmail()
email: string;
@IsPhoneNumber()
phoneNumber: string;
@IsNumber()
qty: string;
}
Using this schema, we can create resolvers and bind them to our useForm
hooks:
const resolver = classValidatorResolver(SandwichPO);
...
const {
register,
handleSubmit,
formState: { errors },
} = useForm({ resolver: resolver });
...
That's it! Everything is now aligned π₯³. Delegating our validation to a 3rd-party library makes our code more organized and robust π€.
latest code from resolver implementation could be found on resolver-implementation
branch
Choosing validation library
When it comes to choosing a library, there are several factors that need to be considered. The rule of thumb that I follow is:
Experience
When selecting a validation library, it is best to choose one that you have used before. This will reduce the learning curve and make implementation easier.
Compatibility with the backend
Building backend services with JavaScript has become quite common nowadays. It would be ideal to use the same validation library on both the front-end and back end to ensure consistent validation using the same schema.
Library size
Keep in mind that the size of the validation library can increase the size of your app and cause longer download times for the user. Therefore, choose a library size that is appropriate for your needs.
Summary; TLDR
Front-end validation could improve user experience, increase security, save resources, and maintain data consistency. Therefore, itβs crucial to have a robust validation mechanism. react-hook-form could help us to standardize our validation strategy and it could even help us to integrate a 3rd-party validation library using the react-hook-form resolvers.
credits:
- Photo by Antonio Borriello: https://www.pexels.com/photo/photo-of-macbook-air-on-a-table-next-to-house-plant-and-picture-frame-1297611/
- Notion AI: https://www.notion.so/product/ai
Source:
Top comments (0)