I began learning react.js a few weeks ago and it has been quite an experience. So, I decided to practice by building a movie website. This article shows how I built a movie app using react-router. Come along with me, sit tight, and enjoy the ride.
what to do?
- Create your app by running
npx create-react-app my-movie-app
And thencd
into the created folder. Good. - Install react-router by running the command
for the purpose of this tutorial, we would also be installing bootstrap and axios. Therefore, we runnpm install react-router-dom
npm install axios --save
Now that that's done we can runnpm install bootstrap --save
npm start
. Cool.
our movie app is going to have 3 components, a Movie component, a MovieDisplay component, which displays the generated movies and a MovieDetails component which gives us information about a particular movie.
In our app.js
we link the Movie and the aboutMovie components by placing them in a <Route>
tag and wrapping them inside the <BrowserRouter>
tag like so.
import React from 'react';
import './index.css';
import 'bootstrap/dist/css/bootstrap.min.css';
import Movies from './components/Movies';
import MovieDetails from './components/MovieDetails';
function App () {
return (
<div className='main'>
<BrowserRouter>
<Route path='/' exact component={Movies} />
<Route path='/MovieDetails/:id' component={MovieDetails} />
</BrowserRouter>
</div>
);
}
export default App;
The <Route>
tags are links between the components and we specify which component to load by using the path attribute. Notice also that we added exact
to the first <Route>
tag, this tells the browser to only return the exact match of the route.
In a file called axios.js
we create our baseUrl for our Api thus
import axios from "axios";
export default axios.create({
baseURL: 'https://yts.mx/',
});
let's style our pages so they look good, in index.css
add the following styles (you can choose to use your own styles)
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Montserrat', sans-serif;
}
body {
background-color: #19181F;
}
.movies {
max-width: 1440px;
margin: 40px auto;
border-radius: 4px;
padding: 32px;
}
.movies img {
-o-object-fit: cover;
object-fit: cover;
-webkit-box-shadow: 0px 4px 24px 3px rgba(92, 163, 163, 0.43);
box-shadow: 0px 4px 24px 3px rgba(92, 163, 163, 0.43);
border-radius: 10px;
}
.movies img:hover {
-webkit-transform: scale(1.1);
transform: scale(1.1);
-webkit-transition: .3s;
transition: .3s;
}
.movies__title {
margin-top: 15px;
color: #FFFFFF;
margin-left: 5px;
padding-top: 5px;
font-weight: 700;
font-size: 20px;
}
.movies__year {
color: #FFFFFF;
margin-left: 5px;
font-weight: 300;
font-size: 20px;
}
.movies button {
margin: 20px auto;
background-color: #cebfbf;
padding: 5px 20px 5px 20px;
border-radius: 5px;
font-size: 300;
border-radius: 6px;
}
.movie-details {
max-width: 1440px;
margin: 40px auto;
padding: 32px;
}
.movie-details img {
-o-object-fit: cover;
object-fit: cover;
-webkit-box-shadow: 0px 4px 24px 3px rgba(0, 0, 0, 0.43);
box-shadow: 0px 4px 24px 3px rgba(0, 0, 0, 0.43);
border-radius: 10px;
}
.movie-details__all {
padding-top: 80px;
color: #fff;
}
.movie-details__all__title {
font-weight: 700;
font-size: 40px;
line-height: 59px;
}
.movie-details__all__year {
font-weight: 300;
font-size: 27px;
line-height: 37px;
}
.movie-details__all__genre {
font-weight: 300;
font-size: 27px;
line-height: 37px;
margin-bottom: 30px;
}
.movie-details__all__description {
font-size: 24px;
line-height: 29px;
}
.movie-details button {
margin: 20px;
background-color: #cebfbf;
padding: 10px 22px 10px 22px;
border-radius: 5px;
}
now that our pages look good we can continue.
In our Movie
component, we call the API to return some data(movies) which will be displayed on the MovieDisplay component.
import React, {useState, useEffect} from 'react';
import yts from 'axios'
import 'bootstrap/dist/css/bootstrap.min.css';
import MovieDisplay from './MovieDisplay';
const myData = (data) => {
return {
error: false,
data,
};
};
const fetchMovie = async (page, limit) => {
try {
let { data: res } = await yts.get(`https://yts.mx/api/v2/list_movies.json/?quality=3D`, {
params: {
page,
limit,
},
});
return myData(res.data.movies);
} catch (err) {
return ({
message: 'Opps! Something went wrong while fetching the data',
});
}
};
const Movies = () => {
const limit = 12;
const [movies, setMovies] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
useEffect(() => {
(async () => {
const { data} = await fetchMovie(currentPage, limit);
setMovies((movies) => [...movies, ...data]);
})();
}, [currentPage]);
return (
<div className= "movies row pt-5">
{movies.map((movie) => {
return <MovieDisplay key={movie.id} movie={movie} />;
})}
<button onClick={() => setCurrentPage(currentPage + 1)}>
Show more movies...
</button>
</div >
);
}
export default Movies;
We also created a button that on click loads another page with more movies. Pretty cool right?.
In MovieDisplay.js
import React from 'react';
import { Link } from 'react-router-dom';
const MovieDisplay = ({ movie }) => {
return (
<div className= "col-md-3">
<Link to={`/MovieDetails/${movie.id}`}>
<img className="img-fluid" src={movie.medium_cover_image} alt={movie.title_long} />
</Link>
<h3 className='movies__title'>{movie.title}</h3>
<p className="movies__year">{movie.year}</p>
</div>
)
}
export default MovieDisplay;
Here have an <img>
tag which generates images from the API. the image tag is enclosed in a <link>
tag which is linked to the id of the movie such that when an image is clicked on it gives us the details of that particular movie.
Finally, in `MovieDetails.js
we fetch the movie details from the API and display the details we need on the page such as the title, genre, and movie description.
import React, { useState, useEffect} from 'react';
import yts from '../axios';
import { Link } from 'react-router-dom';
const MovieDetails = ({ match }) => {
const movie_id = match.params.id;
const [movieDetail, setMovieDetail] = useState([]);
const [genres, setGenres] = useState([]);
useEffect(() => {
(async () => {
const fetchMovieDetails = await yts.get(`api/v2/movie_details.json/`, {
params: {
movie_id,
},
});
setMovieDetail(fetchMovieDetails.data.data.movie);
setGenres(fetchMovieDetails.data.data.movie.genres);
}) ();
},[movie_id] )
return (
<div className="movie-detail">
<div className="row">
<div className="col-sm-5">
<img className="img-fluid" src={movieDetail.large_cover_image} alt={movieDetail.title}/>
</div>
<div className="movie-detail__all col-sm-7">
<h3 className="movie-detail__all__title">{movieDetail.title}</h3>
<h4 className="movie-detail__all__year"> {movieDetail.year}</h4>
<h5 className="movie-detail__all__genre">{genres.map((genre, i) => {
return (<i key={i}>{genre} /</i>);
})}
</h5>
<p className="movie-detail__all__description">{movieDetail.description_full}</p>
<button>
<Link style={{ color: '#000', fontWeight: 'Bold' }} to="/">Back</Link>
</button>
</div>
</div>
</div>
)
}
export default MovieDetails
We also created a button that takes us back to the home page. And our app is up and ready to run.
You can view the live app here [ movie-appfa3b68.netlify.app ].
Thanks for reading my article you're awesome.