Skip to main content
ReactFlix uses a straightforward data management approach combining mock data, custom hooks, and browser storage. This guide covers all aspects of data handling in the application.

Mock Data Structure

The application uses a mock dataset defined in src/data/mockPeliculas.js:1 containing 8 movie objects.

Movie Object Schema

Each movie in the dataset follows this structure:
{
  id: 1,                    // Unique identifier
  title: "Inception",       // Movie title
  director: "Christopher Nolan",
  year: 2010,               // Release year
  genre: "Ciencia Ficción", // Category/genre
  duration: 148,            // Runtime in minutes
  synopsis: "...",          // Plot description
  image: "https://...",     // Poster image URL
  trailerUrl: "https://youtube.com/embed/...",
  price: 12.99,             // Purchase price
  alquilerPrecio: 3.99,     // Rental price
  rating: 8.8,              // User rating (0-10)
  language: "Inglés",       // Primary language
  cast: ["Leonardo DiCaprio", "Joseph Gordon-Levitt", "Elliot Page"]
}
All 8 movies include complete metadata with real poster images and YouTube trailer URLs.

Available Movies

The mock dataset includes:
IDTitleGenreYearRating
1InceptionCiencia Ficción20108.8
2The MatrixCiencia Ficción19998.7
3InterstellarCiencia Ficción20148.6
4The Dark KnightAcción20089.0
5Pulp FictionCrimen19948.9
6The GodfatherDrama19729.2
7AvatarCiencia Ficción20097.8
8GladiatorAcción20008.5

Custom Hooks

usePeliculaSearch Hook

The usePeliculaSearch hook (src/hooks/usePeliculaSearch.js:1) centralizes search and filtering logic. Hook Interface:
const {
  searchTerm,           // Current search query
  setSearchTerm,        // Update search query
  selectedCategory,     // Current genre filter
  setSelectedCategory,  // Update genre filter
  filteredPeliculas,    // Filtered movie results
  loading,              // Loading state
  categories            // Unique genre list
} = usePeliculaSearch(initialPeliculas);
Implementation Details:
1

State Initialization

The hook maintains three pieces of state:
const [searchTerm, setSearchTerm] = useState("");
const [selectedCategory, setSelectedCategory] = useState("");
const [filteredPeliculas, setFilteredPeliculas] = useState(initialPeliculas);
const [loading, setLoading] = useState(false);
2

Category Extraction

Unique genres are extracted from the dataset:
const categories = [
  ...new Set(mockPeliculas.map((pelicula) => pelicula.genre))
];
// Results: ["Ciencia Ficción", "Acción", "Crimen", "Drama"]
3

Filtering Logic

The hook filters movies based on search and category:
useEffect(() => {
  setLoading(true);
  
  const timer = setTimeout(() => {
    let results = mockPeliculas;
    
    // Search filter
    if (searchTerm) {
      results = results.filter((pelicula) =>
        pelicula.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
        pelicula.director.toLowerCase().includes(searchTerm.toLowerCase()) ||
        pelicula.cast.some((actor) =>
          actor.toLowerCase().includes(searchTerm.toLowerCase())
        )
      );
    }
    
    // Category filter
    if (selectedCategory) {
      results = results.filter(
        (pelicula) => pelicula.genre === selectedCategory
      );
    }
    
    setFilteredPeliculas(results);
    setLoading(false);
  }, 300); // 300ms debounce
  
  return () => clearTimeout(timer);
}, [searchTerm, selectedCategory]);
The hook includes a 300ms debounce to simulate network delay and prevent excessive re-renders during typing.
Search Capabilities:
  • Title Search: Case-insensitive matching
  • Director Search: Find movies by director name
  • Cast Search: Search by actor names
  • Genre Filter: Filter by single genre
  • Combined Filters: Search and genre work together

localStorage Integration

ReactFlix uses browser localStorage to persist user transactions across sessions.

Storage Keys

alquileres

Stores rented movies with rental metadata

compras

Stores purchased movies with purchase metadata

Data Persistence Pattern

The application follows a consistent pattern for storage operations:
Pattern used in pages:
useEffect(() => {
  const savedAlquileres = JSON.parse(
    localStorage.getItem('alquileres') || '[]'
  );
  setAlquileres(savedAlquileres);
  setLoading(false);
}, []);
Location: src/pages/AlquileresPage.jsx:8
Pattern used for rentals:
const handleRent = (rentedPelicula) => {
  const newAlquileres = [
    ...alquileres,
    { 
      ...rentedPelicula, 
      alquilerDate: new Date().toISOString() 
    }
  ];
  setAlquileres(newAlquileres);
  localStorage.setItem('alquileres', JSON.stringify(newAlquileres));
};
Location: src/pages/PeliculaDetailPage.jsx:36
Pattern used for rental extensions:
const handleExtendRental = (peliculaId) => {
  const updatedAlquileres = alquileres.map(alquiler => {
    if (alquiler.id === peliculaId) {
      return { ...alquiler, extended: true };
    }
    return alquiler;
  });
  setAlquileres(updatedAlquileres);
  localStorage.setItem('alquileres', JSON.stringify(updatedAlquileres));
};
Location: src/pages/AlquileresPage.jsx:14

Transaction Data Structure

Rental Object:
{
  ...peliculaObject,        // All movie properties
  alquilerDate: "2026-03-06T12:30:00.000Z",
  extended: true            // Added if rental extended
}
Purchase Object:
{
  ...peliculaObject,        // All movie properties
  compraDate: "2026-03-06T12:30:00.000Z"
}
localStorage is synchronous and blocks the main thread. For production apps with large datasets, consider using IndexedDB or a backend API.

State Management Patterns

Component State

ReactFlix uses React’s useState hook for local component state:
Rental Button (src/components/AlquilerButton.jsx:4):
const [isRenting, setIsRenting] = useState(false);

const handleRent = () => {
  setIsRenting(true);
  setTimeout(() => {
    onRent(pelicula);
    setIsRenting(false);
    alert(`Has alquilado ${pelicula.title}...`);
  }, 500);
};
Page Loading (src/pages/PeliculaDetailPage.jsx:13):
const [loading, setLoading] = useState(true);

useEffect(() => {
  const timer = setTimeout(() => {
    const foundPelicula = mockPeliculas.find(m => m.id === parseInt(id));
    setPelicula(foundPelicula);
    setLoading(false);
  }, 500);
  
  return () => clearTimeout(timer);
}, [id]);

if (loading) {
  return <LoadingSpinner />;
}

Props Drilling

Data flows down through props in a unidirectional pattern:
PeliculaDetailPage
  ├── state: { pelicula, alquileres, compras }
  ├── handlers: { handleRent, handlePurchase }

  ├─> AlquilerButton
  │     ├── pelicula (prop)
  │     └── onRent (prop)

  └─> CompraButton
        ├── pelicula (prop)
        └── onPurchase (prop)

Data Fetching Simulation

Although ReactFlix uses mock data, it simulates asynchronous data fetching:
1

Initial Loading State

const [loading, setLoading] = useState(true);
2

Simulated Delay

const timer = setTimeout(() => {
  // Data processing
  setLoading(false);
}, 500);
3

Cleanup

return () => clearTimeout(timer);
This pattern makes it easy to replace mock data with real API calls - just swap the setTimeout with a fetch or axios call.

Extending the Data Layer

Adding New Movies

To add movies to the catalog:
  1. Open src/data/mockPeliculas.js
  2. Add a new object to the array:
export const mockPeliculas = [
  // ... existing movies
  {
    id: 9,
    title: "Your Movie",
    director: "Director Name",
    year: 2024,
    genre: "Genre",
    duration: 120,
    synopsis: "Your synopsis",
    image: "https://your-image-url.com/poster.jpg",
    trailerUrl: "https://www.youtube.com/embed/VIDEO_ID",
    price: 14.99,
    alquilerPrecio: 4.99,
    rating: 8.5,
    language: "Language",
    cast: ["Actor 1", "Actor 2", "Actor 3"]
  }
];
The id field must be unique. Use the next sequential number.

Connecting to a Real API

To replace mock data with a backend:
1

Create API Service

// src/services/api.js
export const fetchPeliculas = async () => {
  const response = await fetch('https://api.yourbackend.com/movies');
  return response.json();
};
2

Update Hook

// src/hooks/usePeliculaSearch.js
import { fetchPeliculas } from '../services/api';

useEffect(() => {
  const loadPeliculas = async () => {
    setLoading(true);
    const data = await fetchPeliculas();
    setFilteredPeliculas(data);
    setLoading(false);
  };
  loadPeliculas();
}, []);
3

Update Transaction Handlers

const handleRent = async (pelicula) => {
  await fetch('https://api.yourbackend.com/rentals', {
    method: 'POST',
    body: JSON.stringify({ peliculaId: pelicula.id })
  });
  // Update local state
};

Best Practices

Immutable Updates

Always create new arrays/objects instead of mutating:
const newAlquileres = [...alquileres, newItem];

Error Handling

Add try-catch blocks when working with localStorage:
try {
  localStorage.setItem('key', JSON.stringify(data));
} catch (e) {
  console.error('Storage failed:', e);
}

Type Safety

Validate data before use:
const data = JSON.parse(
  localStorage.getItem('alquileres') || '[]'
);

Cleanup Effects

Always return cleanup functions:
useEffect(() => {
  const timer = setTimeout(...);
  return () => clearTimeout(timer);
}, [deps]);

Project Structure

Learn about the overall project organization

Customization

Customize data sources and behavior

Build docs developers (and LLMs) love