- Overview
- Features
- System Architecture
- Technical Requirements
- Functional Requirements
- Non-Functional Requirements
- The Difference Between HTML and JSX
Movie Vault is a movie database web service that allows users to find movie information and ratings for a wide range of movies. Users can also add movies to their “favorites” list.
- Display featured movies on page load
- Search for movies by title or actor
- Mark movies as favorites
- Add/remove movies from the favorites list
- Navigate between home and favorites page
- Option to choose between light/dark theme
- Pagination for movie listings
The application will be using client-server architecture. The server will handle API requests and interact with external API, while the client will provide a user interface for interaction.
- Technologies: HTML, CSS, JavaScript
- Library: React
- Technologies: Node.js, Express.js
- Proxy Server: Express.js to handle API requests and secure the TMDBI API key.
- Environment Management: Use environment variable to store API key.
- The client sends a request to the server for data (e.g., search results, movie information).
- The client receives the data and renders it in their browser (e.g., display movie thumbnails, search results).
- The server calls out to TMDB with an embeded API key (request).
- Gets the response from TMDB.
- Server then sends the response back to the client.
Interaction Flow Chart Diagram
:
- HTML, CSS, JavaScript
- Frontend: React
- Backend: Node.js, Express.js
- Use local storage for user's favorite movies
- The application will use the TMDB (Open Movie Database) API to fetch movie data.
- Vercel
Upon loading the website, the page will automatically fetch and display a list of featured movies to provide immediate content for users.
- Movie Thumbnail Cards: Image, title, year, runtime, genre, and ratings (star icons).
- User Interaction: Clicking on a movie thumbnail will provide more detailed information about the movie.
A dialog component (modal) for users to view their list of favorite movies.
- Display all movies that the user has marked as favorites.
- Allow users to remove movies from the favorites list by clicking the favorites icon on the movie thumbnail.
A seperate page that displays movie information (title, year, plot, ratings, director, and runtime).
Navigation:
- Buttons for accessing different pages.
- Modal for viewing the list of favorite movies.
Search input field:
- Display search results dynamically as the user types.
Toggle Light/Dark Theme:
- Allows users to toggle between light and dark theme.
- Store theme selection in local storage. Default to system theme if no selection is stored.
- Transition Effect: Smoothly fades the background color within 0.3 seconds allows for a less abrupt and more visually pleasing effect.
Bookmark / Favorites Icon:
- Ability to save movies in their “favorites” list.
- Favorite movie thumbnails (icon) appears in the navigation bar. Ability to add and remove from favorites.
- Use local storage to store favorites data.
Implement pagination for movie listings.
- Divide movie listings into pages with navigation controls.
- Display a set numbers of movies per page (e.g., 8).
- Prevent API Key Exposure: The TMDB API key must be stored in an environment variable. This is to observe security best practices.
- Accessibility (alt text for screen readers)
- Navigation bar
- Search bar
- Responsiveness (e.g., desktops, tablets, and mobile)
HTML is the standard language used to create web pages. It uses elements like <div>
, <p>
, and <a>
to structure and display content.
JSX (JavaScript XML) is a syntax extension that allows you to write HTML-like code directly within your JavaScript.
JSX allows you to embed JavaScript expressions by wrapping them in curly braces {}
.
{name}
is the JavaScript expression that evaluates to "Sam", and it's embedded within the JSX<p>
element.
const name = 'Sam';
const greeting = <p>Hello, {name}!</p>;
You can use curly braces to include JavaScript logic, such as if statements or ternary operators, directly inside JSX to conditionally render elements.
const isLoggedIn = true;
const greeting = (
<div>
{isLoggedIn ? <p>Welcome back!</p> : <p>Please log in.</p>}
</div>
);
- In JSX, you need to wrap child elements with a parent element, like
<div>...</div>
, or use a fragment<>...</>
to group elements without adding extra nodes to the DOM. - In HTML, you can have multiple sibling elements without needing a wrapper.
<div></div>
<div></div>
// This will cause an error in JSX:
// return (
// <div></div>
// <div></div>
// );
// Correct way in JSX:
return (
<div>
<div></div>
<div></div>
</div>
);
// Or using a Fragment:
return (
<>
<div></div>
<div></div>
</>
);
All elements in JSX must be properly closed.
<img src="image.jpg">
const element = <img src="image.jpg" />;
Using camelCase for attribute names in JSX helps distinguish them from HTML attributes and avoids conflicts with JavaScript reserved words. For example, instead of class
, use className
.
// This will throw syntax error
const element = <div class="container">Hello</div>;
// This is correct and avoids conflict
const element = <div className="container">Hello</div>;
- Visit https://www.themoviedb.org/
- Create an account or log in if you already have one.
- In the settings menu, click "API".
- Click "Apply for API Key" and fill in the requirements of how you intend to use the API. Then click "submit form".
- After your application is approved, your API key will be displayed in the API section.
Note: This project uses TMDB API v3.
- In the root directory, create a new file named '.env'.
- Open the '.env' file and add the following line:
TMDB_API_KEY=your_api_key
- Replace
your_api_key
with the actual API key you copied from TMDB. - Install
dotenv
package:
npm install dotenv
- Load the environment variables at the beginning of your main file (e.g.,
index.js
):
import dotenv from 'dotenv';
dotenv.config();
- Access the API Key in your code, use
process.env.TMDB_API_KEY
.
The key prop plays a crucial role in React as it helps manage lists of elements. It allows React to uniquely identify each element, ensuring that it can render properly in the DOM. By providing unique keys for each element, React can efficiently update and manage the elements in a list, leading to better performance and accurate rendering.
The effect of the 'key' prop:
- When new element is added to a list, React uses the keys to identify which elements have changed, been added or removed.
- This allows React to update only the changed elements without re-rendering the entire list.
<ul>
<li key="1">Strawberry</li>
<li key="2">Grape</li>
<li key="3">Passion Fruit</li>
<li>Orange</li>
</ul>
The Orange
element does not have a key. As a result, React will re-render the entire list because it doesn't know that Orange
is a new addition to the list.
@react
React dependencies handles the following:
- Manage state
- Lifecycle Methods: mounting, updating, unmounting
- Context: share data globally without passing props manually through every level.
- Performance: useMemo and useCallback (Hooks) to memorize values and functions to avoid unnecessary re-renders.
- Refs: Directly access and manipulate DOM elements or store mutable values.
@react-dom
- Provide methods to render React components into the DOM.
- Updates the DOM when component state or props change.
@types/react
- Includes React core functionality such as creating components, managing state and props, lifecycle methods, hooks, and JSX.
- Enables autocomplete and type checking to ensure the types of variables, function parameters, and return values match the expected types.
@types/react-dom
- Covers React DOM APIs such as rendering components to the DOM and handling events related to the DOM.
@vitejs/plugin-react
- A Vite plugin build tool that enables fast refresh in development.
@eslint
- Checks for errors such as typos in variable names or incorrect syntax.
- Automates code formatting, such as correcting indentation or adding missing semicolons.
@eslint-plugin-react
- Plugin that enforces React best practices, such as ensuring components are named properly.
@eslint-plugin-react-hooks
- Checks that you've included all the necessary dependencies when you use Hooks like useEffect or useCallback.
- Ensures that Hooks are at the top level of the function component, not inside loops, conditions, or nested functions.
@eslint-plugin-react-refresh
- Allows you to see changes instantly during development. Provides instant updates.
@vite
- Vite is a development build tool that provides an instant server start, allowing for fast project refreshes.
- With Fast Hot Module Replacement, it allows you to see changes instantly in the browser as you edit your code.
- Updates only the specific module that was changed, rather than reloading the entire page.
@nodemon
- Automatically restarts Node.js application when it detect file changes.
- Eliminate the needs to manually stop and restart the server during development.
- React Context is useful for storing all the data, props and share state globally across multiiple components.
- Eliminates prop drilling (manually pass props through every component).
In the above image, currentPage
, setCurrentPage
,and paginate
were passed as props between components (prop drilling).
- This makes it hard to manage and it's prone to human error since you're manually changing the props in every component file.
createContext()
function is used to create thePaginationContext
.- It allows components to access shared state (
currentPage
andsetCurrentPage
). PaginationProvider
: A context provider component that wraps its child components (paginate
).
- Manages paginate state (
currentPage
andsetCurrentPage
). - Makes this state accessible to any component within
PaginateProvider
. - Child components can use
useContext(PaginateContext)
to access and modifycurrentPage
directly.
import { useContext } from 'react';
import { PaginationContext } from './PaginationProvider';
export const usePaginationContext = () => useContext(PaginationContext);
- The hook retrieves the data from
PaginationContext
.
data includes
currentPage
(page number) andsetcurrentPage
(function to update the page)
usePaginationContext
wrapsuseContext(PaginationContext)
to provide an easy and reusable way to access thePaginationContext
in any component that needs it.
- Allows component to consume the data from a context.
- Retrieves the data provided by a context's
Provider
, allowing you to access the context without needing to pass props through component trees.
debug()
logs the current HTML structure of the rendered component's DOM to the console.- It helps you inspect the elements, attributes, and content currently rendered in the DOM.
This function logs and returns a URL to the Testing Playground website, which provides a visual representation of the current DOM.
- Call
screen.logTestingPlaygroundURL()
within your test at the point where you want to inspect the DOM. - When the test runs, it prints a URL in your terminal that links to Testing Playground, allowing you to see the DOM structure visually.
- Provides suggestions for the most efficient and reliable queries to target specific elements in your tests, helping you select elements based on accessibility best practices.