Building a Movie List App.
Building a Movie List App¶
This app is an interactive single-page application (SPA) built with React, React Router, and a local API. Let’s take a look at its inner workings.
Routing and Navigation¶
At the core of this app is a well-structured routing system defined with
createBrowserRouter. It organizes navigation and allows components to share
layouts and state efficiently.
Here’s the router configuration:
const router = createBrowserRouter([
{
path: '/',
element: <App/>,
errorElement: <ErrorPage/>,
children: [
{
path: '/', element: <Home/>
},
{
path: '/favorites', element: <Favorites/>
},
],
},
{
path: '/credits',
element: <Credits/>,
errorElement: <ErrorPage/>,
},
]);
The App component provides shared context (like the list of movies) to its
child routes using useOutletContext. This approach avoids prop drilling while
keeping the data flow straightforward.
The Navbar component allows seamless navigation between pages using NavLink:
<NavLink className="navlink" to="/" end>Home</NavLink>
<NavLink className="navlink" to="/favorites" end>Favorites</NavLink>
<NavLink className="navlink" to="/credits" end>Credits</NavLink>
This setup means users can navigate without full-page reloads, preserving the SPA experience.
State Management and API Integration¶
The App component serves as the central hub for managing state. It fetches
movies from a local API when the app initializes and provides the movies array
and the addMovie function to its children.
Fetching Movies¶
The movies are retrieved using fetch in a useEffect hook:
useEffect(() => {
fetch('http://localhost:3000/movies')
.then((response) => {
if (!response.ok) throw new Error('Failed to fetch movies');
return response.json();
})
.then((data) => setMovies(data))
.catch((error) => console.error(error));
}, []);
Adding Movies¶
The addMovie function updates the local state with new movie data:
This function is passed to components like MovieForm via useOutletContext,
simplifying data flow and ensuring consistency.
Homepage: Viewing and Adding Movies¶
The homepage (Home.jsx) displays the list of movies and includes a form for
adding new ones.
Displaying Movies¶
The MovieList component iterates over the movies array and renders each movie
using the MovieData component:
<ul>
{moviesArray.map((movie) => (
<MovieData key={movie.id} title={movie.title}
description={movie.synopsis}/>
))}
</ul>
Adding New Movies¶
The MovieForm component captures user input and submits it to the API:
const handleSubmit = (e) => {
e.preventDefault();
if (!title || !synopsis) {
setError('All fields are required!');
return;
}
fetch('http://localhost:3000/movies', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({title, synopsis, favorite: fav}),
})
.then((res) => res.json())
.then((data) => {
addMovie(data);
setTitle('');
setSynopsis('');
})
.catch((err) => console.error('Error adding movie:', err));
};
The form ensures the user provides a title and synopsis before submitting, improving data integrity.
Favorites Page¶
The /favorites route filters and displays movies marked as favorites. The
Favorites component achieves this by using the useOutletContext hook to access
the movies array:
const {movies} = useOutletContext();
const favoriteMovies = movies.filter((movie) => movie.favorite);
return (
<>
<h1>Your Favorite Movies</h1>
{favoriteMovies.length ? (
<MovieList moviesArray={favoriteMovies}/>
) : (
<p>No favorite movies yet.</p>
)}
</>
);
This component reuses MovieList, demonstrating the app’s modular design.
Error Handling¶
Errors are inevitable in any app, and this project includes mechanisms to handle
them gracefully. For instance, if the user navigates to a nonexistent route, the
ErrorPage component displays a friendly message:
export default function ErrorPage() {
const error = useRouteError();
return (
<>
<Navbar/>
<h1>Oops!</h1>
<p>Something went wrong.</p>
<p>
<i>{error.statusText || error.message}</i>
</p>
</>
);
}
This ensures users aren't left staring at a blank screen when something goes wrong.
Conclusion¶
This movie list app showcases the power of React and React Router in building modern, responsive SPAs. With a clean architecture, modular components, and efficient state management, the app balances functionality and maintainability. Its use of shared context and reusable components simplifies data flow, while features like API integration and error handling ensure a friendly user experience.