Formik: Haciendo divertido crear formularios en React
Tags: webdev, react, formik, statemanagement
Published: 2023-12-30 08:26:18

## 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:

```typescript
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>
    )
}

```


## 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.

```typescript
const [ errorDato, setErrorDato ] = useState("")
```
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.)

```typescript
if ( !datoValidado(dato) ) return setErrorDato("Su dato ingresado contiene un error")
```
Luego, verificar si hay un error y mostrarlo.

```typescript
    error={!!errorDato}
    helperText={errorDato}
```

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:

```typescript
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>
    )
}
```

**(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:

```typescript
    <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}
    />
```
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:

```typescript
onSubmit(valores, opcionesFormulario) {
    if ( !datoValidado(valores.dato) ) return opcionesFormulario.setErrors({ dato: "Su dato ingresado contiene un error" })
    subirDato(valores)
    opcionesFormulario.resetForm()
}
```
[Podés leer más de su API aquí.](https://formik.org/docs/api/formik)

## 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:


```typescript
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
     })
```

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í:

```typescript
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>
    )
}

```

## 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](https://formik.org/docs/overview)

[Formik + Material UI](https://formik.org/docs/examples/with-material-ui)