Formularios en React Parte II

Formularios en React Parte II

Introducción

No todos los elementos de formulario funcionan de la misma forma que los <input type="text"> con los que hemos estado trabajando.

Cada uno de estos elementos como <textarea>,<input type="checkbox">,<input type="radio"> tienen sus peculiaridades a la hora de trabajar con ellos en React.

Elementos de formulario

textarea

En HTML, la etiqueta textarea se cierra con su correspondiente etiqueta de cierre y, sea lo que sea que pongamos entre las etiquetas es su valor o value por ejemplo: <textarea> mi texto </textarea>.

Sin embargo, en JSX/React, para representar un elemento textarea podemos cerrarlo en la misma etiqueta e, igual que nuestro inputs de tipo texto, le añadimos una propiedad value por ejemplo: <textarea value={'mi texto'}/>

Todo lo demás, que hemos aprendido previamente en el anterior artículo, permanece igual, salvo el atributo type, que en el caso de <textarea/> es implícito.

Miremos cómo añadir un campo textarea al componente que usamos en la parte I de este artículo usando lo que que acabamos de aprender:

react textarea.png

import React from "react"

export default function Form() {
    const [formData, setFormData] = React.useState(
        {nombre: "", apellidos: "", comentarios: ""}
    )

    console.log(formData);

   function handleChange(eventObject) {
        const nombreInput = eventObject.target.name;
        const valorInput = eventObject.target.value;
        setFormData(estadoAnterior => {
            return {
                ...estadoAnterior,
                [nombreInput]: valorInput
            }
        })
    }

    return (
        <form>
            <input
                type="text"
                placeholder="Nombre"
                onChange={handleChange}
                name="nombre"
                value={formData.nombre}
            />
            <input
                type="text"
                placeholder="Apellidos"
                onChange={handleChange}
                name="apellidos"
                value={formData.apellidos}
            />
            <textarea 
                placeholder="Comentarios"
                onChange={handleChange}
                name="comentarios"
                value={formData.comentarios}
            />

        </form>
    )
}

checkbox

Los checkbox son fundamentalmente diferentes de los demás elementos input de tipo texto con los que hemos estado trabajando hasta ahora, dado que solo almacenan valores booleanos, por lo que no habrá una propiedad value como en los demás inputs. En vez de ello, para controlar el valor del checkbox, se accede a la propiedad "checked", que será true o false dependiendo de si el usuario ha marcado el checkbox o no.

Dado que los checkbox almacenan valores booleanos, vamos añadir a nuestro objeto de estado un campo correspondiente

react checkbox.png

import React from "react"

export default function Form() {
    const [formData, setFormData] = React.useState(
        {
            nombre: "", 
            apellidos: "", 
            email: "", 
            comentarios: "", 
            estaMarcadoTerminos: true
        }
    )

    console.log(formData);

    function handleChange(event) {
        const {name,value,type,checked} = event.target;
        setFormData(prevFormData => {
            return {
                ...prevFormData,
                [name]: type === "checkbox" ? checked : value
            }
        })
    }

    return (
        <form>
            <input
                type="text"
                placeholder="Nombre"
                onChange={handleChange}
                name="nombre"
                value={formData.nombre}
            />
            <input
                type="text"
                placeholder="apellidos"
                onChange={handleChange}
                name="apellidos"
                value={formData.apellidos}
            />

            <textarea 
                value={formData.comentarios}
                placeholder="comentarios"
                onChange={handleChange}
                name="comentarios"
            />
            <input 
                type="checkbox" 
                id="esMarcado" 
                checked={formData.marcado}
                onChange={handleChange}
                name="estaMarcadoTerminos"

            />
            <label htmlFor="esMarcado">Aceptar terminos y condiciones</label>
            <br />
        </form>
    )
}

En nuestra función handleChange a la hora de asignar un valor a nuestra propiedad basada en eventObject.target.name, hacemos referencia aeventObject.target.value, pero con los checkbox no usamos esta propiedad para modificarlo, por lo que podemos usar un operador ternário y verificar si el tipo del input sobre el que se ha producido el evento es de tipo checkbox o no.

Para hacerlo, a la hora de recibir el objeto de evento hemos desestructurado algunas de sus propiedades como name,value,type y checked y luego a la hora de asignar un valor a la propiedad correspondiente, preguntamos si es de tipo checkbox, si lo es,se le asigna el valor de la propiedad checked en caso contrario se le asigna value.

radio buttons

En React, los radio buttons son una combinación de checkbox e inputs de tipo texto.

Analicemos este ejemplo: radiobuttons react.png

import React from "react"

export default function Form() {
    const [formData, setFormData] = React.useState(
        {
            nombre: "", 
            apellidos: "", 
            email: "", 
            comentarios: "", 
            estaMarcadoTerminos: true
        }
    )

    console.log(formData);

    function handleChange(event) {
        const {name,value,type,checked} = event.target;
        setFormData(prevFormData => {
            return {
                ...prevFormData,
                [name]: type === "checkbox" ? checked : value
            }
        })
    }

    return (
        <form>
            <input
                type="text"
                placeholder="Nombre"
                onChange={handleChange}
                name="nombre"
                value={formData.nombre}
            />
            <input
                type="text"
                placeholder="apellidos"
                onChange={handleChange}
                name="apellidos"
                value={formData.apellidos}
            />

            <textarea 
                value={formData.comentarios}
                placeholder="comentarios"
                onChange={handleChange}
                name="comentarios"
            />
            <input 
                type="checkbox" 
                id="esMarcado" 
                checked={formData.marcado}
                onChange={handleChange}
                name="estaMarcadoTerminos"

            />
            <label htmlFor="esMarcado">Aceptar terminos y condiciones</label>
            <br />
            <br/>

            <fieldset>
            <legend>Ocupación</legend>

            <input 
            type="radio"
            id="radio1"
            name="ocupacion"
            value="Desempleado"
            checked={formData.ocupacion === "Desempleado"}
            onChange={handleChange}
            />
            <label htmlFor="radio1"> Desempleado </label>
            <br/>



            <input 
            type="radio"
            id="radio2"
            name="ocupacion"
            value="Media jornada"
            checked={formData.ocupacion === "Media jornada"}
            onChange={handleChange}
            />
            <label htmlFor="radio2"> Media jornada </label>
            <br/>


            <input 
            type="radio"
            id="radio3"
            name="ocupacion"
            value="Jornada completa"
            checked={formData.ocupacion === "Jornada completa"}
            onChange={handleChange}
            />
            <label htmlFor="radio3"> Jornada completa </label>


            </fieldset>
        </form>
    )
}

En este ejemplo, he añadido un fieldset el cual contiene 3 radio buttons, cada radio button tiene una etiqueta label, las vinculo con el radio button a traves del atributo htmlFor.

Además, a cada radio button le añado un atributo name ya que en handleChange usamos esta propiedad del objeto de evento para determinar qué propiedad de nuestro objeto de estado debe modificarse. Le ponemos el mismo nombre para que sólo uno de ellos pueda ser seleccionado por vez, además los 3 modifican la misma propiedad de nuestro estado.

Adicionalmente,le asignamos un value por lo cual estaremos escuchando a cambios en los inputs, y esto ocurra, cogerá el valor de este input específico y lo asignará como el valor de la propiedad ocupacion de nuestro estado.

A la hora de hacerlo un componente controlado, los radio buttons funcionan diferente,dado que necesitamos dotarle de un valor como hemos visto 2 líneas más arriba.

Dado que los radio buttons son como un híbrido entre checkbox y input type text, también podemos usar su propiedad checked para determinar si está marcado o no.

Como hemos visto en el código checked={formData.ocupacion === "desempleado"},checked={formData.ocupacion === "Media jornada"} y checked={formData.ocupacion === "Jonada completa"}, esto hace que ahora el valor sea controlado por el estado.

La expresión retornará true y estará marcado el radio button sólo si hemos hecho clic en él y se ha modificado el valor de la propiedad ocupacion a una de las tres opciones ("Desempleado","Media jornada" o "Jornada completa"). En caso contrario retornará false y checked estará desmarcado.

select

En HTML,para acceder al valor de una opción que ha sido seleccionada, debemos acceder a su propiedad selected, en cambio en React añadimos una propiedad value a la etiqueta <select>, por lo que podemos transformala en una input controlado.

react select .png

import React from "react"

export default function Form() {
    const [formData, setFormData] = React.useState(
        {
            nombre: "",
            apellidos: "",
            email: "",
            comentarios: "",
            estaMarcadoTerminos: true,
            colorFavorito:""
        }
    )

    console.log(formData);

    function handleChange(event) {
        const { name, value, type, checked } = event.target;
        setFormData(prevFormData => {
            return {
                ...prevFormData,
                [name]: type === "checkbox" ? checked : value
            }
        })
    }

    return (
        <form>
            <input
                type="text"
                placeholder="Nombre"
                onChange={handleChange}
                name="nombre"
                value={formData.nombre}
            />
            <input
                type="text"
                placeholder="apellidos"
                onChange={handleChange}
                name="apellidos"
                value={formData.apellidos}
            />

            <textarea
                value={formData.comentarios}
                placeholder="comentarios"
                onChange={handleChange}
                name="comentarios"
            />
            <input
                type="checkbox"
                id="esMarcado"
                checked={formData.marcado}
                onChange={handleChange}
                name="estaMarcadoTerminos"

            />
            <label htmlFor="esMarcado">Aceptar terminos y condiciones</label>
            <br />
            <br />

            <fieldset>
                <legend>Ocupación</legend>

                <input
                    type="radio"
                    id="radio1"
                    name="ocupacion"
                    value="Desempleado"
                    checked={formData.ocupacion === "Desempleado"}
                    onChange={handleChange}
                />
                <label htmlFor="radio1"> Desempleado </label>
                <br />



                <input
                    type="radio"
                    id="radio2"
                    name="ocupacion"
                    value="Media jornada"
                    checked={formData.ocupacion === "Media jornada"}
                    onChange={handleChange}
                />
                <label htmlFor="radio2"> Media jornada </label>
                <br />


                <input
                    type="radio"
                    id="radio3"
                    name="ocupacion"
                    value="Jornada completa"
                    checked={formData.ocupacion === "Jornada completa"}
                    onChange={handleChange}
                />
                <label htmlFor="radio3"> Jornada completa </label>


            </fieldset>
            <br />
            <label htmlFor="favColor">¿Cuál es tu color favorito?</label>
            <br />
            <select 
                id="favColor"
                value={formData.colorFavorito}
                onChange={handleChange}
                name="colorFavorito"
            >
                <option value="">-- Elige una opción --</option>
                <option value="rojo">Rojo</option>
                <option value="naranja">Naranja</option>
                <option value="amarillo">Amarillo</option>
            </select>
        </form>
    )
}

Aqui podemos seguir usando nuestro manejador de eventos handleChange por lo que también podemos añadirle una propiedad name (de igual nombre a nuestra nueva propiedad en el objeto de estado).

La única observación que haría aquí es que en un principio inicializamos la propiedad colorFavorito con un string vacío, por lo que añadimos una option que por defecto tenga el valor de un string vacío. Yo he decidido colocarla como una "título" (-- Elige una opción --)