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:
ID Title Genre Year Rating 1 Inception Ciencia Ficción 2010 8.8 2 The Matrix Ciencia Ficción 1999 8.7 3 Interstellar Ciencia Ficción 2014 8.6 4 The Dark Knight Acción 2008 9.0 5 Pulp Fiction Crimen 1994 8.9 6 The Godfather Drama 1972 9.2 7 Avatar Ciencia Ficción 2009 7.8 8 Gladiator Acción 2000 8.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:
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 );
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"]
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:
Reading from localStorage
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:
Trailer Modal (src/pages/PeliculaDetailPage.jsx:14):const [ showTrailer , setShowTrailer ] = useState ( false );
// Show trailer
< button onClick = { () => setShowTrailer ( true ) } > Ver Tráiler </ button >
// Render modal
{ showTrailer && (
< TrailerModal
pelicula = { pelicula }
onClose = { () => setShowTrailer ( false ) }
/>
)}
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:
Initial Loading State
const [ loading , setLoading ] = useState ( true );
Simulated Delay
const timer = setTimeout (() => {
// Data processing
setLoading ( false );
}, 500 );
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:
Open src/data/mockPeliculas.js
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:
Create API Service
// src/services/api.js
export const fetchPeliculas = async () => {
const response = await fetch ( 'https://api.yourbackend.com/movies' );
return response . json ();
};
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 ();
}, []);
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