Formularios
Los formularios son una parte fundamental de muchas aplicaciones web, permitiendo a los usuarios interactuar y enviar datos. En React, el manejo de formularios tiene algunas particularidades que lo diferencian de cómo se manejan otros elementos del DOM.
Introducción a los Formularios en React
React ofrece dos enfoques principales para trabajar con formularios:
-
Formularios no controlados: En este enfoque, los datos del formulario son manejados por el propio DOM. React tiene menor control sobre los valores de los campos del formulario.
-
Formularios controlados: Aquí, el estado de React controla los valores de los elementos del formulario. Cada cambio en el input actualiza el estado del componente.
Cada enfoque tiene sus propias ventajas y casos de uso, que exploraremos en detalle.
Recursos
En la sección usaremos los siguientes recursos:
Formularios no controlados
Los formularios no controlados son aquellos donde los datos del formulario son manejados directamente por el DOM, en lugar de ser controlados por el estado de React.
Características principales
- Los datos del formulario son gestionados por el propio DOM.
- Se utilizan cuando se necesita integrar React con código no-React o en formularios muy simples.
- Generalmente, se recomienda usar componentes controlados, pero los no controlados tienen sus casos de uso.
Acceso a elementos del formulario
Para acceder a los elementos de un formulario no controlado en React:
- No se usan selectores tradicionales de JavaScript: Evitamos
document.getElementById()
o similares. - Se utilizan referencias (refs): Proporcionan una forma segura de acceder a nodos del DOM en React.
Uso de Referencias (Refs)
Las refs permiten acceder a nodos del DOM o elementos React creados en el método de renderizado.
Hook useRef: Hook que devuelve un objeto ref mutable cuya propiedad .current se mantiene durante toda la vida del componente.
Veamos un ejemplo de formulario no controlado:
import React, { useRef } from 'react'
const FormNoControlados = () => {
const formulario = useRef(null)
//console.log(formulario)
const handleSubmit = e => {
e.preventDefault()
const datos = new FormData(formulario.current)
console.log(...datos.entries())
const objetoDatos = Object.fromEntries([...datos.entries()])
console.log(objetoDatos);
const {title, description, state} = objetoDatos
if (!title.trim() || !description.trim() || !state.trim()) {
console.log("error!!!!!!!!!!");
return
}
console.log("Enviando objeto datos al server!!!!")
}
return (
<div>
<p>Form No Controlados</p>
<form ref={formulario} onSubmit={handleSubmit}>
<input
name="title"
placeholder="Introduce nombre de la tarea"
type="text"
className="form-control mb-2"
defaultValue="Tarea 1"
/>
<textarea
name="description"
placeholder="Introduce la descripcion"
className="form-control mb-2"
defaultValue="Descripción de la tarea 1"
/>
<select
name="state"
className="form-control mb-2"
defaultValue="Pendiente"
>
<option value="pendiente">Pendiente</option>
<option value="completada">Completada</option>
</select>
<button
type="submit"
className='btn btn-primary'
>
Añadir
</button>
</form>
</div>
)
}
export default FormNoControlados
Formularios controlados
Los componentes React que rendericen un formulario también pueden controlar lo que pasa en ese formulario en tiempo real. Esto nos permite manejar y validar la entrada del usuario de manera más precisa y reactiva.
Características principales:
- El estado de React controla los valores de los elementos del formulario.
- Cada cambio en un input actualiza el estado.
- Permite validación y manipulación de datos en tiempo real
Veamos un ejemplo:
import React, { useState } from 'react'
const FormularioControlado = () => {
// 1. Creamos un estado por cada input.
const [title, setTitle] = useState("")
const [description, setDescription] = useState("")
const [state, setState] = useState("pendiente")
const handleSubmit = (e) => {
e.preventDefault()
console.log(`Enviando ${title}, ${description}, y ${state} al servidor ...`)
}
return (
<div>
<p>Formularios Controlados</p>
<form onSubmit={handleSubmit}>
<input
name="title"
placeholder="Introduce nombre de la tarea"
type="text"
className="form-control mb-2"
// 2. Para asociar el estado con el valor del campo, usamos el value del input.
value = {title}
// 3. Tal y como hemos visto en el error de la consola, la propiedad value tiene que estar pendiente de un onChange.
// onChange={e => console.log(e.target.value)}
onChange={e => setTitle(e.target.value)}
/>
<textarea
name="description"
placeholder="Introduce la descripcion"
className="form-control mb-2"
value = {description}
onChange={e => setDescription(e.target.value)}
/>
<select
name="state"
className="form-control mb-2"
value = {state}
onChange={e => setState(e.target.value)}
>
<option value="pendiente">Pendiente</option>
<option value="completada">Completada</option>
</select>
<button
type="submit"
className='btn btn-primary'
>
Añadir
</button>
</form>
</div>
)
}
export default FormularioControlado
Si tuvieramos un formulario con muchos inputs, la solución anterior sería algo tediosa. Vamos a mejorar el código:
import React, { useState } from 'react'
const FormularioControlado = () => {
const [todo, setTodo] = useState({
title: "Todo 01",
description:"Descripción 01",
state:"pendiente"
})
const handleSubmit = (e) => {
e.preventDefault()
console.log(`Enviando ${todo.title}, ${todo.description}, y ${todo.state} al servidor ...`)
}
const handlechange = e => {
console.log(e.target.value)
console.log(e.target.name)
setTodo({
...todo,
[ e.target.name]:e.target.value
})
}
return (
<div>
<p>Formularios Controlados</p>
<form onSubmit={handleSubmit}>
<input
name="title"
placeholder="Introduce nombre de la tarea"
type="text"
className="form-control mb-2"
value = {todo.title}
onChange={handlechange}
/>
<textarea
name="description"
placeholder="Introduce la descripcion"
className="form-control mb-2"
value = {todo.description}
onChange={handlechange}
/>
<select
name="state"
className="form-control mb-2"
value = {todo.state}
onChange={handlechange}
>
<option value="pendiente">Pendiente</option>
<option value="completada">Completada</option>
</select>
<button
type="submit"
className='btn btn-primary'
>
Añadir
</button>
</form>
</div>
)
}
export default FormularioControlado
Añadimos ahora un checkbox, el cual tiene un comportamiento algo diferente:
import React, { useState } from 'react'
const FormularioCheckBox = () => {
const estadoInicial = {
title: "",
description: "",
state: "pendiente",
todoCheck:false
}
const [todo, setTodo] = useState(estadoInicial)
const [error, setError] = useState(false)
const handleSubmit = (e) => {
e.preventDefault()
const {title, description} = todo
if (!title.trim() || !description.trim()) {
setError(true);
console.log(error)
return
}
console.log("Enviando objeto datos al server!!!!")
setError(false)
setTodo(estadoInicial)
}
// const handleChange = (e) => {
// //console.log(e.target)
// setTodo({
// ...todo,
// [e.target.name]: e.target.type === 'checkbox' ? e.target.checked : e.target.value
// })
// }
const handleChange = (e) => {
const {name, value, checked, type} = e.target
setTodo({
...todo,
[name]: type === 'checkbox' ? checked : value
})
}
return (
<div>
<p>Formularios Controlados con CheckBox</p>
{
error ? <div className="alert alert-danger">Campos obligatorios</div> : null
}
<form onSubmit={handleSubmit}>
<input
name="title"
placeholder="Introduce nombre de la tarea"
type="text"
className="form-control mb-2"
// onChange={e =>setTodo({...todo, title: e.target.value})}
onChange={e=>handleChange(e)}
value={todo.title}
/>
<textarea
name="description"
placeholder="Introduce la descripcion"
className="form-control mb-2"
onChange={e=>handleChange(e)}
value={todo.description}
/>
<select
name="state"
className="form-control mb-2"
onChange={e=>handleChange(e)}
value={todo.state}
>
<option value="pendiente">Pendiente</option>
<option value="completada">Completada</option>
</select>
<div className="form-check mb-2">
<input
className="form-check-input"
type="checkbox"
name="todoCheck"
checked={todo.todoCheck}
onChange={e => handleChange(e)}
/>
<label
className="form-check-label"
htmlFor="flexCheckDefault">
</label>
Prioridad
</div>
<button
type="submit"
className='btn btn-primary'
>
Añadir
</button>
</form>
</div>
)
}
export default FormularioCheckBox