DEV Community

Marcelo Martínez
Marcelo Martínez

Posted on

Formik: Haciendo divertido crear formularios en React

Entendiendo los formularios

Una parte esencial de toda aplicación son los formularios, es la manera en que recibimos los datos de parte del usuario, mismos datos que hacen la aplicación interactiva. Podemos decir que lo que hace a un frontend ser una aplicación y no una landing page es la manera en que transformamos estos datos en información.

Por lo tanto, podemos partir de un punto: Vamos a necesitar crear muchos formularios en nuestras aplicaciones. Y, como en todo lo repetitivo, debemos recordar el principio DRY (Don´t Repeat Yourself). A la hora de utilizar un framework de Javascript puede haber mucha monotonía para crear formularios porque siempre son 3 pasos:

  1. Crear un estado para el dato que queremos.
  2. Agregar ese dato en nuestro input.
  3. Validarlo.

Veamos un ejemplo de ello en React, utilizando Material UI:

function Formulario() {

    //1. Crear el estado
    const [ dato, setDato ] = useState("")

    //2. Validarlo
    const [ errorDato, setErrorDato ] = useState("")
    function subirDatos() {
        if ( !datoValidado(dato) ) return setErrorDato("Su dato ingresado contiene un error")
        return subirDato(dato)
    }

    return (
        <form onSubmit={subirDatos} >
            <TextField
                //3. Agregar ese dato en nuestro input
                value={dato}
                onChange={e => setDato(e.target.value)}
                error={!!errorDato}
                helperText={errorDato}
            />
            <Button
                type="submit"
            >
                Confirmar
            </Button>
        </form>
    )
}

Enter fullscreen mode Exit fullscreen mode

El problema

Sucede que estos 3 simples pasos se descompusieron en pasos más complejos (por no decir tediosos).
Primero, tuvimos que crear un estado extra para el mensaje de error.

const [ errorDato, setErrorDato ] = useState("")
Enter fullscreen mode Exit fullscreen mode

Segundo, tuvimos que ejecutar una lógica para validar ese dato y dar un mensaje de error correspondiente. Recordemos que en la práctica, tendrá que ser un mensaje de error distinto por cada error en un dato. (Ej: Si contiene un caracter desconocido, si supera cierta longitud, etc.)

if ( !datoValidado(dato) ) return setErrorDato("Su dato ingresado contiene un error")
Enter fullscreen mode Exit fullscreen mode

Luego, verificar si hay un error y mostrarlo.

    error={!!errorDato}
    helperText={errorDato}
Enter fullscreen mode Exit fullscreen mode

Entonces por cada nuevo dato que agreguemos en nuestro formulario debemos repetir todos estos pasos, además de procurar que no nos confundamos de entre tantos estados al utilizarlos.

Personalmente hallo la monotonía en este proceso frustrante y tediosa entre más datos se agregan. Traté alternativas, como que todos los datos estén en un objeto dentro de un estado, consciente de que trae ciertos problemas de optimización, pero no me sentí satisfecho.

Por años había escuchado de alternativas como formik, pero a veces los desarrolladores nos cerramos a lo que ya conocemos. Y es normal, especialmente en JavaScript que sale un nuevo framework cada semana alegando solucionar los problemas de todos los anteriores y ser una solución universal. Pero luego de años, le di una oportunidad y solo puedo decir: es divertido crear formularios.

Formik: La solución?

Para conocer a Formik instalemos dos paquetes:
npm i formik yup o yarn add formik yup (pronto veremos por qué necesitamos yup)

Formik nos va a gestionar todos los datos de nuestro formulario, además de brindarnos algunas conveniencias. Recreemos el formulario anterior con formik:

import { useFormik } from "formik";

function FormularioFromik() {

    const formik = useFormik({
        //1. Crear el estado
        initialValues: {
            dato: ""
        },
        //2.Validarlo
        onSubmit(valores, opcionesFormulario) {
            if ( !datoValidado(valores.dato) ) return opcionesFormulario.setErrors({ dato: "Su dato ingresado contiene un error" })
            return subirDato(valores)
        }
    })

    return (
        <form onSubmit={formik.handleSubmit} >
            <TextField
                //3.Agregar ese dato en nuestro input
                name="dato"
                value={formik.values.dato}
                onChange={formik.handleChange}
                error={!!formik.errors.dato}
                helperText={formik.errors.dato}
            />
            <Button type="submit">
                Confirmar
            </Button>
        </form>
    )
}
Enter fullscreen mode Exit fullscreen mode

(Nota: La forma en que Formik le da seguimiento a cada input es por su name, por lo que nunca olvidés poner su name igual al de su estado correspondiente.)

Así de sencillo abstraímos los pasos 1 y 2 en uno solo ya que una misma función nos provee manejo del estado del dato, de su error y la función de submit. Pero esta librería no se queda ahí pues nos ofrece otras cosas que van a mejorar la experiencia de los usuarios.

Un ejemplo de ello es que no nos gustaría mostrar un error en un campo si el usuario ni lo ha presionado, o viceversa, mostrar ese error si el usuario presiona el campo pero no lo llena. Con formik lo podemos hacer agregando estas simples líneas de código:

    <TextField
         name="dato"
         value={formik.values.dato}
         onChange={formik.handleChange}
         //A partir de aquí ⬇
         onBlur={formik.handleBlur}
         error={formik.touched.dato&& !!formik.errors.dato}
         helperText={formik.touched.dato && formik.errors.dato}
    />
Enter fullscreen mode Exit fullscreen mode

Ahora el error solo se va a mostrar si el usuario tocó el campo.

También si nos gustaría reiniciar el estado del formulario una vez el usuario ingresó sus datos (por ejemplo, como en un inventario donde se agregan varios productos con un solo formulario) simplemente utilizamos:

onSubmit(valores, opcionesFormulario) {
    if ( !datoValidado(valores.dato) ) return opcionesFormulario.setErrors({ dato: "Su dato ingresado contiene un error" })
    subirDato(valores)
    opcionesFormulario.resetForm()
}
Enter fullscreen mode Exit fullscreen mode

Podés leer más de su API aquí.

Ahora, ¿Y cómo validamos los distintos errores posibles de cada dato?

Aquí es donde entra Yup.

Yup nos permite crear esquemas de validación de nuestros datos, muy similares a las validaciones de las tablas en una Base de Datos. Podemos validar el tipo de dato, longitud, caracteres especiales, entre mucho más. Y es así de sencillo:

import * as yup from "yup"

const esquemaValidacion = yup.object({
    dato: yup
        .string()
        .required("El dato es requerido.")
        .email("Debe haber un formato de correo (correo@ejemplo.com)")
        .matches(/[^-_/.,\\p{L}0-9 ]+/, "Solo se aceptan caracteres en español.")
        .max(40, "El dato no puede ser mayor a 40 caracteres")
        .default("")
})

function FormularioFromik() {
     const formik = useFormik({
             //1. Crear el estado
             initialValues: esquemaValidacion.getDefault(),
             validationSchema: esquemaValidacion
     })
Enter fullscreen mode Exit fullscreen mode

Creamos un esquema donde definimos nuestro valor dato, le dimos un valor por default y pusimos restricciones. Para agregar un mensaje de error en cada restricción es tan sencillo como pasarlo en un argumento.
Formik puede manejar esquemas de validación, así que solo lo agregamos en nuestro objeto así validationSchema: esquemaValidacion, y así de fácil cada input va a mostrar sus mensajes de error correspondiente.

Nuestro ejemplo completo se vería así:

import { Button, TextField } from "@mui/material"
import { useFormik } from "formik";
import * as yup from "yup"

const esquemaValidacion = yup.object({
    dato: yup
        .string()
        .required("El dato es requerido.")
        .email("Debe haber un formato de correo (correo@ejemplo.com)")
        .matches(/[^-_/.,\\p{L}0-9 ]+/, "Solo se aceptan caracteres en español.")
        .max(40, "El dato no puede ser mayor a 40 caracteres")
        .default("")
})

function FormularioFromik() {

    const formik = useFormik({
        //1. Crear el estado
        initialValues: esquemaValidacion.getDefault(),
        validationSchema: esquemaValidacion,
        //2.Validarlo
        onSubmit(valores, opcionesFormulario) {
            subirDato(valores)
            opcionesFormulario.resetForm()
        }
    })

    return (
        <form onSubmit={formik.handleSubmit} >
            <TextField
                //3.Agregar ese dato en nuestro input
                name="dato"
                value={formik.values.dato}
                onChange={formik.handleChange}
                onBlur={formik.handleBlur}
                error={formik.touched.dato&& !!formik.errors.dato}
                helperText={formik.touched.dato && formik.errors.dato}
            />
            <Button type="submit" disabled={formik.isSubmitting}>
                Confirmar
            </Button>
        </form>
    )
}

Enter fullscreen mode Exit fullscreen mode

Conclusión

Formik junto con Yup nos permite mantener un código escalable, limpio y seguro (respecto a errores de colocación de variables) a su vez dando una divertida experiencia programando. Si buscás quitarle lo tedioso a crear formularios diría que es la solución que necesitás.

Te animo a probarlo para que volvás a amar crear formularios en React. :)

Referencias

Documentación de formik

Formik + Material UI

Top comments (0)