Skip to content

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:

function addMovie(newMovie) {
    setMovies([...movies, newMovie]);
}

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.