16 - Formulario para Crear Película
Objetivo
Crear un formulario en React que permita agregar nuevas películas a nuestra aplicación, con validación de estado y retroalimentación al usuario.
1. Estructura del Componente de Formulario
// En nuestro componente principal (MovieForm.js)
import React, { useState } from 'react';
import './MovieForm.css'; // Opcional: para estilos
const MovieForm = ({ onCreateMovie }) => {
// Estado para manejar el formulario
const [movie, setMovie] = useState({
title: '',
cover: '',
synopsis: '',
year: ''
});
// Estado para controlar si la película fue enviada
const [submitted, setSubmitted] = useState(false);
// Manejar cambios en los inputs
const handleInputChange = (e) => {
const { name, value } = e.target;
setMovie({
...movie,
[name]: value
});
};
// Manejar el envío del formulario
const handleSubmit = (e) => {
e.preventDefault();
onCreateMovie(movie);
setSubmitted(true);
};
// Resetear el formulario para crear otra película
const resetForm = () => {
setMovie({
title: '',
cover: '',
synopsis: '',
year: ''
});
setSubmitted(false);
};
return (
<div className="form-container">
{/* Operador ternario para mostrar diferentes interfaces */}
{submitted ? (
// Mensaje de éxito después de enviar
<div className="success-message">
<h4>¡Has creado exitosamente tu película!</h4>
<button
className="btn btn-secondary"
onClick={resetForm}
>
Crear otra película
</button>
</div>
) : (
// Formulario para crear una nueva película
<form className="movie-form" onSubmit={handleSubmit}>
<div className="form-group">
<label htmlFor="title">Título</label>
<input
type="text"
className="form-control"
id="title"
name="title"
value={movie.title}
onChange={handleInputChange}
required
/>
</div>
<div className="form-group">
<label htmlFor="cover">URL de la Portada</label>
<input
type="text"
className="form-control"
id="cover"
name="cover"
value={movie.cover}
onChange={handleInputChange}
required
/>
</div>
<div className="form-group">
<label htmlFor="synopsis">Sinopsis</label>
<textarea
className="form-control"
id="synopsis"
name="synopsis"
value={movie.synopsis}
onChange={handleInputChange}
rows="4"
required
/>
</div>
<div className="form-group">
<label htmlFor="year">Año</label>
<input
type="number"
className="form-control"
id="year"
name="year"
value={movie.year}
onChange={handleInputChange}
min="1900"
max="2024"
required
/>
</div>
<button type="submit" className="btn btn-primary">
Guardar Película
</button>
</form>
)}
</div>
);
};
export default MovieForm;2. Explicación del Código
Estructura Condicional (Operador Ternario)
{submitted ? ( ... ) : ( ... )}submitted=true: Muestra mensaje de éxitosubmitted=false: Muestra el formulario
Estado del Formulario
const [movie, setMovie] = useState({
title: '',
cover: '',
synopsis: '',
year: ''
});Un solo estado para manejar todos los campos
Cada propiedad corresponde a un input
Manejo de Cambios
const handleInputChange = (e) => {
const { name, value } = e.target;
setMovie({
...movie,
[name]: value
});
};Actualiza solo el campo modificado
Usa
[name]para identificar dinámicamente el campo
Envío del Formulario
const handleSubmit = (e) => {
e.preventDefault(); // Evita recarga de página
onCreateMovie(movie); // Llama a la función padre
setSubmitted(true); // Cambia a vista de éxito
};3. Estilos CSS (MovieForm.css)
.form-container {
max-width: 600px;
margin: 2rem auto;
padding: 2rem;
background-color: #f8f9fa;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.movie-form {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.form-group {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.form-group label {
font-weight: 600;
color: #333;
}
.form-control {
padding: 0.75rem;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 1rem;
transition: border-color 0.3s ease;
}
.form-control:focus {
outline: none;
border-color: #007bff;
box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
}
.btn {
padding: 0.75rem 1.5rem;
border: none;
border-radius: 4px;
font-size: 1rem;
cursor: pointer;
transition: background-color 0.3s ease;
}
.btn-primary {
background-color: #007bff;
color: white;
}
.btn-primary:hover {
background-color: #0056b3;
}
.btn-secondary {
background-color: #6c757d;
color: white;
}
.btn-secondary:hover {
background-color: #545b62;
}
.success-message {
text-align: center;
padding: 2rem;
}
.success-message h4 {
color: #28a745;
margin-bottom: 1.5rem;
}4. Integración con el Componente Padre
// App.js o componente padre
import React, { useState } from 'react';
import MovieForm from './components/MovieForm';
import MovieList from './components/MovieList';
function App() {
const [movies, setMovies] = useState([]);
// Función para crear nueva película
const createMovie = (newMovie) => {
// En una aplicación real, aquí harías una petición POST
setMovies([...movies, { ...newMovie, id: Date.now() }]);
// En desarrollo, puedes verificar en la consola
console.log('Película creada:', newMovie);
};
return (
<div className="App">
<header>
<h1>Administrador de Películas</h1>
</header>
<main>
<MovieForm onCreateMovie={createMovie} />
<MovieList movies={movies} />
</main>
</div>
);
}
export default App;5. Prueba del Formulario
Pasos para probar:
Abre las herramientas de desarrollo (F12)
Ve a la pestaña "Network" para ver las peticiones
Abre la consola para ver logs
Llena el formulario:
Título: "Perfume de Mujer"
Portada: URL de la imagen
Sinopsis: "Un militar ciego..."
Año: 1992
Haz clic en "Guardar Película"
Resultado esperado:
Mensaje de éxito: "¡Has creado exitosamente tu película!"
Botón "Crear otra película" para resetear
La nueva película aparece en la lista
6. Mejoras Adicionales
Validación Avanzada:
const [errors, setErrors] = useState({});
const validateForm = () => {
const newErrors = {};
if (!movie.title.trim()) {
newErrors.title = 'El título es requerido';
}
if (!movie.year || movie.year < 1900 || movie.year > 2024) {
newErrors.year = 'Año inválido';
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};Manejo de Errores de API:
const handleSubmit = async (e) => {
e.preventDefault();
if (!validateForm()) return;
try {
await onCreateMovie(movie);
setSubmitted(true);
} catch (error) {
console.error('Error al crear película:', error);
alert('Hubo un error al guardar la película');
}
};7. Consejos y Buenas Prácticas
Accesibilidad:
Usa
htmlForen labelsAgrega
aria-labeldonde sea necesario
UX/UI:
Feedback visual al usuario
Loading states durante peticiones
Mensajes claros de error/success
Performance:
Usa
useCallbackpara funciones pasadas como propsConsidera
React.memopara componentes de formulario
Seguridad:
Valida datos en frontend y backend
Sanitiza inputs para prevenir XSS
8. Próximos Pasos
En la siguiente clase:
Crear una tabla para mostrar todas las películas
Implementar funcionalidad de edición
Agregar filtros y búsqueda
Implementar paginación
Nota sobre plugins de React/JSX: Algunos editores requieren extensiones específicas para autocompletado de JSX. Para VS Code, recomiendo:
ES7+ React/Redux/React-Native snippets
Babel JavaScript
Prettier - Code formatter
¡Recuerda dejar en los comentarios si encontraste algún plugin útil para el autocompletado de JSX
Comentarios
Publicar un comentario