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.
- Es recomendable conocer los fundamentos de JS antes de comenzar este curso.
- curso de JS gratis
- curso de git y github
Planificación
- Formularios no controlados
- Formularios controlados
Código final github
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 });
// utilizando el callback
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;
// utilizando el callback
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;
// utilizando el callback
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.