Skip to content

React Hooks Formularios

En esta sección aprenderemos a trabajar con formularios en React, utilizando Hooks.

⭐ Videos Premium ⭐

Esta sección es parte del curso en Udemy. Si quieres acceder a ella, puedes comprar el curso en Udemy: React + Firebase by bluuweb.

O bien también puedes visualizar los videos siendo miembro en Youtube. Accede a miembros del canal aquí y accede a los videos de esta sección.

WARNING

Planificación

  • Formularios no controlados
  • Formularios controlados
  • React Hook Form

Recursos

Extensiones Navegador (React Developer Tools):

Formulario no controlado

  • uncontrolled: En la mayoría de los casos, te recomendamos usar Componentes controlados para implementar formularios.
  • En un componente controlado, los datos del formulario son manejados por un componente React.
  • La alternativa son los componentes no controlados, donde los datos del formulario son manejados por el propio DOM.
  • Para escribir un componente no controlado, puedes usar una referencia para que obtengas los valores del formulario desde el DOM.

Referencias y el DOM

  • refs: Las referencias proporcionan una forma de acceder a los nodos del DOM o a elementos React creados en el método de renderizado.
  • useRef: useRef devuelve un objeto ref mutable cuya propiedad .current se inicializa con el argumento pasado (initialValue). El objeto devuelto se mantendrá persistente durante la vida completa del componente.
jsx
import { useRef } from "react";

const FormularioNoControlado = () => {
    const formulario = useRef(null);

    // Formulario no controlado
    const handleSubmit = (e) => {
        console.log(formulario.current);
        e.preventDefault();
    };

    return (
        <div className="container mt-2">
            <form onSubmit={handleSubmit} ref={formulario}>
                <input
                    className="form-control mb-2"
                    type="text"
                    placeholder="Ingrese un TODO"
                    name="todoNombre"
                    defaultValue="Tarea #01"
                />
                <textarea
                    className="form-control mb-2"
                    type="text"
                    placeholder="Ingrese un TODO"
                    name="todoDescripcion"
                    defaultValue="Descripción tarea #01"
                />
                <select
                    className="form-control mb-2"
                    name="todoEstado"
                    defaultValue="completado"
                >
                    <option value="pendiente">Pendiente</option>
                    <option value="completado">Completado</option>
                </select>
                <button className="btn btn-primary" type="submit">
                    Agregar
                </button>
            </form>
        </div>
    );
};

export default FormularioNoControlado;

FormData

jsx
import { useRef } from "react";

const FormularioNoControlado = () => {
    const formulario = useRef(null);

    // Formulario no controlado
    const handleSubmit = (e) => {
        // console.log(formulario.current);
        e.preventDefault();

        const datos = new FormData(formulario.current);

        // spread operator: permite a un elemento iterable ser expandido
        // copia cada uno de sus elementos
        console.log(...datos.entries());

        // El método Object.fromEntries() transforma una lista de pares con [clave-valor] en un objeto.
        const objetoDatos = Object.fromEntries([...datos.entries()]);

        // console.log(objetoDatos);
        if (!objetoDatos.todoNombre.trim()) {
            return console.log("vacío");
        }

        console.log("pasó las validaciones!");
        formulario.current.reset();
    };

    return (
        <div className="container mt-2">
            <form onSubmit={handleSubmit} ref={formulario}>
                <input
                    className="form-control mb-2"
                    type="text"
                    placeholder="Ingrese un TODO"
                    name="todoNombre"
                    // defaultValue="Tarea #01"
                />
                <textarea
                    className="form-control mb-2"
                    type="text"
                    placeholder="Ingrese un TODO"
                    name="todoDescripcion"
                    // defaultValue="Descripción tarea #01"
                />
                <select
                    className="form-control mb-2"
                    name="todoEstado"
                    // defaultValue="completado"
                >
                    <option value="pendiente">Pendiente</option>
                    <option value="completado">Completado</option>
                </select>
                <button className="btn btn-primary" type="submit">
                    Agregar
                </button>
            </form>
        </div>
    );
};

export default FormularioNoControlado;

Formularios controlados

  • controlled
  • Los componentes React que rendericen un formulario también controlan lo que pasa en ese formulario con las subsecuentes entradas del usuario.
  • Ahora vamos a poder detectar los campos input en tiempo real.

Ejemplo casi casi:

jsx
import { useState } from "react";

const Form = () => {
    const [todo, setTodo] = useState({
        todoNombre: "",
        todoDescripcion: "",
        todoEstado: "pendiente",
    });

    const handleSubmit = (e) => {
        e.preventDefault();

        console.log(todo);
    };

    return (
        <div className="container mt-2">
            <form onSubmit={handleSubmit}>
                <input
                    className="form-control mb-2"
                    type="text"
                    placeholder="Ingrese un TODO"
                    name="todoNombre"
                    value={todo.todoNombre}
                    onChange={(e) =>
                        setTodo({ ...todo, todoNombre: e.target.value })
                    }
                />
                <textarea
                    className="form-control mb-2"
                    type="text"
                    placeholder="Ingrese un TODO"
                    name="todoDescripcion"
                    value={todo.todoDescripcion}
                    onChange={(e) =>
                        setTodo({ ...todo, todoDescripcion: e.target.value })
                    }
                />
                <select
                    className="form-control mb-2"
                    name="todoEstado"
                    value={todo.todoEstado}
                    onChange={(e) =>
                        setTodo({ ...todo, todoEstado: e.target.value })
                    }
                >
                    <option value="pendiente">Pendiente</option>
                    <option value="completado">Completado</option>
                </select>
                <button className="btn btn-primary" type="submit">
                    Agregar
                </button>
            </form>
        </div>
    );
};

export default Form;

Ahora si se ve más bonito:

jsx
import { useState } from "react";

const Form = () => {
    const [todo, setTodo] = useState({
        todoNombre: "",
        todoDescripcion: "",
        todoEstado: "pendiente",
    });

    const handleSubmit = (e) => {
        e.preventDefault();

        console.log(todo);
    };

    const handleChange = (e) => {
        // console.log(e.target.name);
        // console.log(e.target.value);
        // setTodo({ ...todo, [e.target.name]: e.target.value });
        setTodo((prev) => ({
            ...prev,
            [e.target.name]: e.target.value,
        }));
    };

    // console.log(todo);

    return (
        <div className="container mt-2">
            <form onSubmit={handleSubmit}>
                <input
                    className="form-control mb-2"
                    type="text"
                    placeholder="Ingrese un TODO"
                    name="todoNombre"
                    value={todo.todoNombre}
                    onChange={handleChange}
                />
                <textarea
                    className="form-control mb-2"
                    type="text"
                    placeholder="Ingrese un TODO"
                    name="todoDescripcion"
                    value={todo.todoDescripcion}
                    onChange={handleChange}
                />
                <select
                    className="form-control mb-2"
                    name="todoEstado"
                    value={todo.todoEstado}
                    onChange={handleChange}
                >
                    <option value="pendiente">Pendiente</option>
                    <option value="completado">Completado</option>
                </select>
                <button className="btn btn-primary" type="submit">
                    Agregar
                </button>
            </form>
        </div>
    );
};

export default Form;

Checkbox

jsx
<div className="form-check">
    <input
        className="form-check-input"
        type="checkbox"
        id="flexCheckDefault"
        checked={todo.todoCheck}
        onChange={handleChange}
        name="todoCheck"
    />
    <label className="form-check-label" htmlFor="flexCheckDefault">
        Dar prioridad
    </label>
</div>
jsx
const [todo, setTodo] = useState({
    todoNombre: "",
    todoDescripcion: "",
    todoEstado: "pendiente",
    todoCheck: false,
});

const handleChange = (e) => {
    const { name, value, checked, type } = e.target;

    setTodo((prev) => ({
        ...prev,
        [name]: type === "checkbox" ? checked : value,
    }));
};

console.log(todo);

Pequeña validación

jsx
const [error, setError] = useState(false);

const handleSubmit = (e) => {
    e.preventDefault();

    const { todoNombre, todoDescripcion } = todo;

    // pequeña validación
    if (!todoNombre.trim() || !todoDescripcion.trim()) {
        console.log("campos vacíos");
        setError(true);
        return;
    } else {
        setError(false);
    }

    // Enviar todo a un array!
};

const PintarError = () => (
    <div className="alert alert-danger">Todos los campos obligatorios</div>
);
jsx
<div className="container mt-2">
    <h2>Formulario</h2>
    {error && <PintarError />}
    ...
</div>

Todo junto:

jsx
import { useState } from "react";

const Form = () => {
    const [todo, setTodo] = useState({
        todoNombre: "",
        todoDescripcion: "",
        todoEstado: "pendiente",
        todoCheck: false,
    });

    const [error, setError] = useState(false);

    const handleChange = (e) => {
        const { name, value, checked, type } = e.target;

        setTodo((prev) => ({
            ...prev,
            [name]: type === "checkbox" ? checked : value,
        }));
    };

    const handleSubmit = (e) => {
        e.preventDefault();

        const { todoNombre, todoDescripcion } = todo;

        // pequeña validación
        if (!todoNombre.trim() || !todoDescripcion.trim()) {
            console.log("campos vacíos");
            setError(true);
            return;
        } else {
            setError(false);
        }

        // Enviar todo a un array!
    };

    const PintarError = () => (
        <div className="alert alert-danger">Todos los campos obligatorios</div>
    );

    return (
        <div className="container mt-2">
            <h2>Formulario</h2>
            {error && <PintarError />}
            <form onSubmit={handleSubmit}>
                <input
                    className="form-control mb-2"
                    type="text"
                    placeholder="Ingrese un TODO"
                    name="todoNombre"
                    value={todo.todoNombre}
                    onChange={handleChange}
                />
                <textarea
                    className="form-control mb-2"
                    type="text"
                    placeholder="Ingrese un TODO"
                    name="todoDescripcion"
                    value={todo.todoDescripcion}
                    onChange={handleChange}
                />
                <select
                    className="form-control mb-2"
                    name="todoEstado"
                    value={todo.todoEstado}
                    onChange={handleChange}
                >
                    <option value="pendiente">Pendiente</option>
                    <option value="completado">Completado</option>
                </select>
                <div className="form-check">
                    <input
                        className="form-check-input"
                        type="checkbox"
                        id="flexCheckDefault"
                        checked={todo.todoCheck}
                        onChange={handleChange}
                        name="todoCheck"
                    />
                    <label
                        className="form-check-label"
                        htmlFor="flexCheckDefault"
                    >
                        Dar prioridad
                    </label>
                </div>
                <button className="btn btn-primary" type="submit">
                    Agregar
                </button>
            </form>
        </div>
    );
};

export default Form;

Validación

Si lo que estás buscando es una solución completa incluyendo validación, tener en cuenta los campos visitados y manejar el envío del formulario, Formik es una de las opciones populares.

También existen otras como: react hook form la cual veremos a continuación.

React hook Form

  • Estamos trabajando en esta sección 👷‍♂️👷‍♀️
  • 👍 al video 😍