Cómo validar formularios en React, sin lágrimas
Tabla de contenido
Formik what? #
Formik es uno de los paquetes de software de código abierto más útiles que podemos encontrar en el repositorio de npm
(o yarn
, como gustes). En palabras de uno de sus creadores:
Admítamoslo, los formularios requiren de muchísimo código en React. Para poner las cosas peor, la mayoría de utilidades para construirlos hacen muchísima, demasiada magia que frecuentemente trae un costo al desempeño — @JaredPalmer1
Para Jared y compañía había un beneficio en estandarizar los componentes de entrada y cómo los datos fluían a través del Form. Es la razón de ser de Formik.
Preparando el escenario #
Haremos uso del famosísimo toolchain create-react-app
. Ve a tu línea de comandos y ejecuta:
npx create-react-app formik-sample
cd formik-sample
npm start
Ya tenemos la base para trabajar nuestra aplicación React, pero necesitamos un form a validar. Para eso instalaremos el siguiente paquete:
npm install bootstrap reactstrap --save
Este paquete componetiza la mayoría de utilidades del popular framework CSS Bootstrap, lo cuál acelera nuestra habilidad de construir el layout del formulario. Una vez completa la instalación, copia y pega este código en tu archivo src/App.js
:
1import React from "react";
2import { Container, Button, Form, FormGroup, Label, Input, Card, CardBody, CardHeader, FormFeedback } from "reactstrap";
3
4function Formulario() {
5 return (
6 <Container className="p-5">
7 <Card>
8 <CardHeader></CardHeader>
9 <CardBody>
10 <Form>
11 <h1>Form</h1>
12 <FormGroup>
13 <Label for="name">Name</Label>
14 <Input type="text" name="name" placeholder="Woody Allen" />
15 </FormGroup>
16 <FormGroup>
17 <Label for="email">Email</Label>
18 <Input
19 type="email"
20 name="email"
21 placeholder="contoso@domain.com"
22 />
23 </FormGroup>
24 <FormGroup>
25 <Label for="password">Password</Label>
26 <Input
27 type="password"
28 name="password"
29 placeholder="Provide a password"
30 />
31 </FormGroup>
32 <FormGroup>
33 <Label for="bio">Text Area</Label>
34 <Input type="textarea" name="bio" />
35 </FormGroup>
36
37 <Button type="submit">Submit</Button>
38 </Form>
39 </CardBody>
40 </Card>
41 </Container>
42 );
43}
44
45export default Formulario;
También, añade está línea en el archivo src/index.js
:
1import React from "react";
2import ReactDOM from "react-dom";
3
4import "bootstrap/dist/css/bootstrap.css";
Deberías ver un formulario similar al de la imagen:
En adición a estos dos archivos, crearemos un tercero para escribir dos funciones que serán de mucha importacia al implementar Formik
. srs/helpers.js
:
1export function validate(values) {
2 const errors = {};
3
4 // Name
5 if (!values.name) errors.name = "Required";
6
7 // Email
8 const emailRgx = !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i;
9 if (!values.email) errors.email = "Required";
10 else if (emailRgx.test(values.email)) errors.email = `Invalid email address`;
11
12 // Password
13 if (!values.password) errors.password = `Required`;
14 else if (`${values.password}`.length < 7)
15 errors.password = `Password must be larger than 7 characters`;
16
17 return errors;
18}
19
20export function onSubmit() {
21 setTimeout(() => {
22 alert(JSON.stringify(values, null, 2));
23
24 setSubmitting(false);
25 }, 250);
26}
Las cuáles importamos en srs/App.js
:
1import React from "react";
2import { Container, Button, Form, FormGroup, Label, Input, Card, CardBody, CardHeader, FormFeedback } from "reactstrap";
3import { validate, onSubmit } from "./helpers.js";
Formik #
La instalación es muy sencilla, simplemente es otro paquete en npm
.
npm install formik --save
Luego de instalarlo, lo importamos en src/App.js
.
1import React from "react";
2import { Formik } from "formik";
Formik
es el componente principal de la librería, se mantiene al tanto del estado del formulario y provee valores, métodos y manejadores de eventos a través del parámetro props
. Es indispensable que el formulario sea encapsulado por esta etiqueta <Formik>
. Visualicemos una implementación básica de la librería con el código siguiente:
6function Formulario() {
7 return (
8 <Container className="p-5">
9 <Card>
10 <CardHeader></CardHeader>
11 <CardBody>
12 <Formik
13 initialValues={{
14 name: ``,
15 email: ``,
16 password: ``,
17 bio: ``,
18 }}
19 validate={validate}
20 onSubmit={onSubmit}
21 >
22 {(props) => {
23 const { values, errors, touched, handleChange, handleBlur, handleSubmit, isSubmitting } = props;
24 return (
25 <Form onSubmit={handleSubmit}>
26 <h1>Form</h1>
27 <FormGroup>
28 <Label for="name">Name</Label>
29 <Input
30 type="text"
31 name="name"
32 placeholder="Woody Allen"
33 invalid={errors.name && touched.name}
34 onChange={handleChange}
35 onBlur={handleBlur}
36 value={values.name}
37 />
38 <FormFeedback>{errors.name}</FormFeedback>
39 </FormGroup>
40 <FormGroup>
41 <Label for="email">Email</Label>
42 <Input
43 type="email"
44 name="email"
45 placeholder="contoso@domain.com"
46 invalid={errors.email && touched.email}
47 onChange={handleChange}
48 onBlur={handleBlur}
49 value={values.email}
50 />
51 <FormFeedback>{errors.email}</FormFeedback>
52 </FormGroup>
53 <FormGroup>
54 <Label for="password">Password</Label>
55 <Input
56 type="password"
57 name="password"
58 placeholder="Provide a password"
59 invalid={errors.password && touched.password}
60 onChange={handleChange}
61 onBlur={handleBlur}
62 value={values.password}
63 />
64 <FormFeedback>{errors.password}</FormFeedback>
65 </FormGroup>
66 <FormGroup>
67 <Label for="bio">Text Area</Label>
68 <Input
69 type="textarea"
70 name="bio"
71 onChange={handleChange}
72 onBlur={handleBlur}
73 value={values.name}
74 />
75 </FormGroup>
76
77 <Button type="submit" disabled={isSubmitting}>
78 {isSubmitting ? `Loading` : `Submit`}
79 </Button>
80 </Form>
81 );
82 }}
83 </Formik>
84 </CardBody>
85 </Card>
86 </Container>
87 );
88}
Para que Formik pueda mantenerse al tanto de los cambios de valores en el formulario y, por ende, ser capaz de validarlos, es necesario proveer una colección de valores iniciales, ó initialValues
, que esté relacionados por nombre a los diferentes <Input>
s. En el código anterior, definimos 4 controles: name
, email
, password
y bio
. Líneas 14-17, 31, 44, 57 y 70.
Es en base a esa configuración que los demás métodos y variables contenidas en props
permitirán que Formik haga su magia. Línea 23.
No debemos olvidar que estamos en React, por lo qué debemos manejar el cambio de los valores en los controles. Esto lo logramos con los manejadores provistos por Formik: handleChange
y handleBlur
. Sin olvidar que debemos renderizar el valor adecuado en el control, para eso haremos uso de la propiedad values
. Líneas 34-36, 47-49, 60-62 y 71-73.
También el tag <Formik>
en sí mismo necesita de ciertas propiedades para funcionar, entre ellas:
initialValues
: Le dice a Formik de cuales valores debe mantenerse al tanto y buscar sus respectivos controles en el formulario.validate
: Esta función recibe como parametro losvalues
de los cuáles la librería está al tanto para ser validados. Línea 19.onSubmit
: Quizá la función que más te interese de la librería. Es aquí dónde definimos que debe suceder si nuestro formulario ha sido validado con éxito. Por ejemplo, postear los valores a un servicio del backend. Línea 20.
Al hacer clic en Submit
, sin completar un solo <Input>
, deberías ver algo similar a esto:
Listo, ya hemos validado el formulario.
Simplifiquemos el código #
Seguramente tienes alguna de estas preguntas en tu cabeza:
- ¿Por qué repetir la asignación de
handleChange
yhandleBlur
? y - ¿Por qué validar manualmente cada propiedad paso a paso?
Creéme, yo también me hice esas preguntas.
Para fortuna de quién tenga que mantener este formulario, hay una forma más sencilla de definir las validación y los controles de estado.
Formik Field #
Para reducir el número de veces que repetimos la asignación de los manejadores de eventos, vamos a importar otro elemento de la librería Formik. En el archivo src/App.js
:
1import React from "react";
2import { Formik, Field } from "formik"; // + Field
Este componente nos permite ahorrarnos la asignación de manejadores y valor directamente al componente. Lo único que debemos hacer, y ya que estamos usando reacstrap
, es asignarlo a la propiedad tag
de cada elemento <Input>
. Formik se encargará del resto.
29 <Input
30 type="text"
31 name="name"
32 placeholder="Woody Allen"
33 invalid={errors.name && touched.name}
34 onChange={handleChange}
35 onBlur={handleBlur}
36 value={values.name}
37 />
29 <Input
30 type="text"
31 name="name"
32 placeholder="Woody Allen"
33 invalid={errors.name && touched.name}
34 tag={Field}
35 />
Es imprescindible que la propiedad name
del <Input>
tenga el mismo nombre a una propiedad en initialValues
, como lo explicamos antes.
Formik + Yup #
Yup
es una librería que nos permite construir esquemas de conversión y validación de una manera sintáctica. El punto más fuerte de Yup
es el nível de expresividad que las validaciones pueden alcanzar, sencillas o complejas.
De acuerdo a la documentación de Formik
, Yup
es un ciudadano de primera clase en la librería por lo que posee una prop
especial en el elemento <Formik>
llamada validationSchema
.
$ npm install yup --save
Una vez instalamos Yup
, lo importamos en el archivo src/App.js
:
1import React from "react";
2import { Formik, Field } from "formik";
3import * as Yup from "yup"; // Importando Yup
Regresemos al código del componente Formulario en el archivo src/App.js
. Justo en la propiedades del elemento <Formik>
vamos a eliminar la propiedad validate
y en su lugar asignaremos la propiedad validationSchema
. Así:
13<Formik
14 initialValues={{
15 name: "",
16 email: "",
17 password: "",
18 bio: "",
19 }}
20 validationSchema={Yup.object().shape({
21 name: Yup.string().required("Required"),
22 email: Yup.string().email("Invalid email").required("Required"),
23 password: Yup.string().min(8, "Password is too short").required("Required"),
24 })}
25 onSubmit={onSubmit}
26>
Por último, podemos llenar nuestro formulario y ver que una vez validado, el callback
en la propiedad onSubmit
del elemento <Formik>
es ejecutada.
Antes de irme #
Espero este post te sea de mucha ayuda. En mi experiencia con React, Formik me ha facilitado el validar formularios al proveerme de métodos que me permiten ajustar a cada momento los valores y las validaciones necesarias. La expresividad del código es de gran valor ya que permite escalar y/o mantener de una forma sencilla sin reinventar la rueda para en cada escenario.
Referencias #
Esta cita es parte de la introducción escrita por Jared en el sitio Formik Docs. ↩︎