Validación de formularios con React Hook Forms y Zod
Como desarrolladores Frontend una parte importante en nuestro trabajo es hacer peticiones al Backend, ya sea una solicitud de datos, una comprobación de status o actualizar/crear información, esta última es importante porque no podemos enviar cualquier cosa esperando una respuesta exitosa. Por ejemplo imagina un formulario para iniciar sesión que pide correo y contraseña, fácilmente podemos hacer rápido un formulario que registre los input y mandar la información por una petición, pero de esta manera podemos cometer muchos fallos ya sea permitiendo que envié cualquier texto como correo o una contraseña de solo 3 caracteres, si bien el Backend tiene validaciones de su lado no es recomendable dejarle toda la responsabilidad, pues no solo usamos recursos innecesarios sino que hacemos creer al usuario que la información que proporcionó es válida cuando va a ser rechazada de todos modos.
Con estos puntos en mente es necesario tener un buen sistema de formularios para evitar esos inconvenientes, para eso te mostraré cómo con 2 sencillas librerías puedes ahorrarte estos dolores de cabeza
React Hook Form
RHF es una de las librerías más populares del entorno React, que nos permite tener un mayor control sobre los formularios, como saber cuando se cambia un valor, si los datos ya son validos para hacer el submit, entre otros métodos y hooks que facilitan nuestra experiencia de desarrollo.
Zod
Probablemente la librería más popular para validar tipos, objetos y schemas en general de Javascript, pudiendo comprobar si los datos que dimos son los correctos y flexible para declarar tipos de datos. No solo se usa del lado del cliente, es popular para validar datos en el propio backend.
Instalar dependencias
npm install react-hook-form zod @hookform/resolvers
@hookform/resolvers
nos ayudará a combinar Zod con RHF
Hook para validar formularios
Este hook nos servirá a no repetir el mismo patrón en cada formulario, así solamente tendremos que invocarlo y enviarle el esquema a validar
// useZForm.ts
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import { DefaultValues, useForm } from "react-hook-form";
export interface useZFormProps<T> {
schema: z.ZodType<T>;
defaultValues: DefaultValues<T>;
}
const useZForm = <T extends Object>({
schema,
defaultValues,
}: useZFormProps<T>) => {
const methods = useForm<T>({
resolver: zodResolver(schema),
defaultValues,
});
return { methods, ...methods };
};
export default useZForm;
Donde:
useZFormProps
definimos que queremos el esquema a evaluar durante el ciclo de vida del formulario y los valores por defecto de éste, el cual es opcionalmethods
es el resultado del hook de RHF donde recibimos todos los métodos y valores necesarios para empezar a trabajar en nuestro formularioreturn { methods, ...methods }
para mas facilidad regreso elmethods
y la desestructuración de este para no repetirmethods
en cada llamada
FormProvider
Ahora vamos a crear un componente que dentro del children
se maneje el formulario
// FormProvider.tsx
import { FormProvider as Form, UseFormReturn } from "react-hook-form";
type FPProps = {
children: React.ReactNode;
methods: UseFormReturn<any>;
onSubmit?: VoidFunction;
};
export default function FormProvider ({ children, onSubmit, methods }: FPProps) {
return (
<Form {...methods}>
<form onSubmit={onSubmit}>
{children}
<form/>
</Form>
)
}
Con este sencillo componente podemos empezar a poner nuestros inputs, pero falta añadir el control de los inputs
RHFInput
// RHFInput.tsx
import { Controller, useFormContext } from "react-hook-form";
type Props = HTMLInputElement & {
name: string;
};
export default function RHFInput({ name, helperText, ...other }: Props) {
const { control } = useFormContext();
return (
<Controller
name={name}
control={control}
render={({ field }) => (
<input
{...field}
value={
typeof field.value === "number" && field.value === 0
? ""
: field.value
}
{...other}
/>
)}
/>
);
}
Este componente nos permitirá fácilmente controlar el input del usuario
Formulario de login
Con estos componentes ahora podemos hacer un formulario seguro para el usuario
// Login.tsx
import { z } from "zod";
import {useZForm, FormProvider, RHFInput} from '@/components/form';
export default Login () {
const formSchema = z.object({
email: z.string().email({ message: "Correo no valido" }),
password: z.string().min(8,{ message: "La contraseña debe ser de mínimo 8 caracteres" })
})
const {
methods,
handleSubmit,
formState: { isValid, isSubmitting },
} = useZForm({ schema: formSchema, defaultValues: { email: '', password: '' } })
const onSubmit = ({ email, password }: z.infer<typeof formSchema>) => {
// Código para enviar los datos al back y recibir el usuario o error de login
}
return(
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
<RHFInput name="email" placeholder="correo@ejemplo.com"/>
<RHFInput name="password" placeholder="*****"/>
<button type="submit" disabled={!isValid}>Iniciar sesión</button>
</FormProvider>
)
}
Conclusión
React Hook Form y Zod son excelentes herramientas que facilitan la experiencia del usuario y del desarrollador, no solo son flexibles sino muy fáciles de integrar, hace ya mas de un año que empecé a utilizar este combo tanto en mi trabajo como en proyectos personales y sin duda alguna es sorprendente lo sencillo que se volvió para mi trabajar con formularios, si tienes la oportunidad de integrarlo a tus proyectos no no te arrepentirás.
Recuerda visitar la documentación de estas librerías para estar mejor informado: