Hello π, For my first post on dev.to, I decided to share with you a small tuto about creating a Movie App with React and TypeScript.
Demo https://movie-app-majuran.netlify.app π
Source code available on Github : Here !
Setup
Before starting coding let's setup our project.
Generate project with CreateReactApp:
I often (to not say always π) use Create React App to initiate my React projects.
In order to generate our project run :
npx create-react-app my-movie-app --template typescript
API :
The frontend will have to fetch the data from an API, I choose TMDb π¬ : It's free, you just need to create an account to get your API key
Please use your API key as an environment variable, in .env
file :
REACT_APP_API_KEY=YOUR_API_KEY
The configuration is done, let's start coding. π»
State Management
Our app components are going to communicate between them.
To handle this, we need a state management, for that, we are going to combine the context API
and state hooks
In case, you are not familiar with react hooks. I encourage you, to read β‘οΈ React Hooks and Context API
// create context
export const MoviesContext = React.createContext<{
movies: Movie[];
updateMovies: Function;
}>({
movies: [],
updateMovies: Function,
});
function App() {
// get default values
useEffect(() => {
discoverMovies()
.then(setMovies)
.catch((_) => setMovies([]));
}, []);
// use state hook
const [movies, setMovies] = useState<Movie[]>([]);
return (
// Provide to the all project:
// - movies an array of Movie,
// - updateMovies : function to update the movies list
<MoviesContext.Provider value={{ movies, updateMovies: setMovies }}>
<div className="App">
<Header></Header>
<Catalog></Catalog>
</div>
</MoviesContext.Provider>
);
}
Components
Donβt be afraid to split components into smaller components. (React Doc)
In React it's important to split the UI by components, so let's check how many components do we need :
As you can see, three components are standing out:
Header
Search
Catalog
A question to ask when creating a component:
What my component should do ?! π€
Header
The header component is pretty simple, it contains the project title and the Search component.
export const Header = () => {
return (
<div className="header">
<h1 className="header_title">Movie App</h1>
<div className="header_search">
<Search></Search>
</div>
</div>
);
};
Search
The search component should:
- show an input field
- save user search
- query the API with this search
- update the movie list
In order to save the state of our input, we will use in this component the useState
hook.
export const Search = () => {
const [search, setSearch] = useState("");
// Consume our context to get updateMovies function
const { updateMovies } = useContext(MoviesContext);
const handleOnSubmit = (event: React.FormEvent) => {
event.preventDefault();
if (search) {
searchMovies(search).then((movies) => {
updateMovies(movies);
});
}
return (
<div>
<form name="form" onSubmit={(e) => handleOnSubmit(e)} noValidate>
<input
type="text"
name="movie"
className="search__input"
placeholder="Search movie ... "
value={search}
onChange={(e) => setSearch(e.target.value)}
/>
</form>
</div>
);
};
Catalog
The catalog component should:
- get movies from the context
- loop on this array
export const Catalog = () => {
// Consume the context to get list of movies.
const { movies } = useContext(MoviesContext);
return (
<div className="catalogContainer">
{movies.map((movie) => (
<div className="catalog_item" key={movie.id}>
<div className="catalogitemimg">
<img src={movie.picture || imgPlaceholder} alt={movie.title} />
<div className="catalogitemresume">{movie.resume}</div>
</div>
<div className="catalogitemfooter">
<div className="catalogitemfootername">
{movie.title} ({new Date(movie.date).getFullYear()})
</div>
<div className="catalogitemfooter_rating">{movie.rating}</div>
</div>
</div>
))}
</div>
);
};
fetch() data from API
In order to fetch the API for data, let's create a service :
const movieApiBaseUrl = "https://api.themoviedb.org/3";
export function searchMovies(search: string): Promise<Movie[]> {
return fetch(
</span><span class="p">${</span><span class="nx">movieApiBaseUrl</span><span class="p">}</span><span class="s2">/search/movie?query=</span><span class="p">${</span><span class="nx">search</span><span class="p">}</span><span class="s2">&api_key=</span><span class="p">${</span><span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">REACT_APP_API_KEY</span><span class="p">}</span><span class="s2">
)
.then((res) => res.json())
.catch((_) => {
return [];
});
}
export function discoverMovies(): Promise<Movie[]> {
return fetch(
</span><span class="p">${</span><span class="nx">movieApiBaseUrl</span><span class="p">}</span><span class="s2">/discover/movie?sort_by=popularity.desc&api_key=</span><span class="p">${</span><span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">REACT_APP_API_KEY</span><span class="p">}</span><span class="s2">
)
.then((res) => res.json())
.then((response) => mapResult(response.results))
.catch((_) => {
return [];
});
}
Conclusion
With this tutorial, I tried to present to you some important concept like :
- React Context API
- React hooks
- function fetch to make API calls
To go further
Some more ideas to improve your skills:
- handle No result
- use Redux
- pagination
Source :
- You can clone this project on Github
Top comments (5)
The Castle app is a powerful tool designed to enhance digital security by providing advanced threat detection and risk management features. It operates by monitoring user activity across various platforms, identifying unusual behavior that could indicate potential security threats.
castleappk.org/
Noice
Thanks
Gonna try this out! Might be a fun baseline for an XR app were building. Cheers!
Thanks for reading π