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

jsx
// 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)

jsx
{submitted ? ( ... ) : ( ... )}
  • submitted=true: Muestra mensaje de éxito

  • submitted=false: Muestra el formulario

Estado del Formulario

jsx
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

jsx
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

jsx
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)

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

jsx
// 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:

  1. Abre las herramientas de desarrollo (F12)

  2. Ve a la pestaña "Network" para ver las peticiones

  3. Abre la consola para ver logs

  4. Llena el formulario:

    • Título: "Perfume de Mujer"

    • Portada: URL de la imagen

    • Sinopsis: "Un militar ciego..."

    • Año: 1992

  5. 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:

jsx
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:

jsx
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

  1. Accesibilidad:

    • Usa htmlFor en labels

    • Agrega aria-label donde sea necesario

  2. UX/UI:

    • Feedback visual al usuario

    • Loading states durante peticiones

    • Mensajes claros de error/success

  3. Performance:

    • Usa useCallback para funciones pasadas como props

    • Considera React.memo para componentes de formulario

  4. 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:

  1. ES7+ React/Redux/React-Native snippets

  2. Babel JavaScript

  3. Prettier - Code formatter

¡Recuerda dejar en los comentarios si encontraste algún plugin útil para el autocompletado de JSX

Comentarios

Entradas más populares de este blog

Axios para Principiantes - Guía Paso a Paso

15-Tutorial: Crear Película en React con useState

Tutorial de React para Principiantes