Final
.
├── node_modules/
├── public/
├── src/
│ ├── components/
| | ├── Banner.js
| | ├── Category.js
| | ├── Movie.js
| | └── Search.js
│ ├── pages/
| | ├── Details.js
| | ├── Home.js
| | └── Results.js
| ├── utils/
| | ├── axios.js
| | └── requests.js
│ ├── App.js
│ └── index.js
├── .env
├── .gitignore
├── package-lock.json
└── package.json
// src/utils/axios.js
import axios from "axios";
const instance = axios.create({
baseURL: "https://api.themoviedb.org/3",
});
export default instance;
// src/utils/requests.js
const requests = {
fetchTrending: `/trending/all/week?api_key=${process.env.REACT_APP_API_KEY}&language=en-US`,
fetchNowPlaying: `/movie/now_playing?api_key=${process.env.REACT_APP_API_KEY}&language=en-US`,
fetchPopular: `/movie/popular?api_key=${process.env.REACT_APP_API_KEY}&language=en-US`,
fetchTopRated: `/movie/top_rated?api_key=${process.env.REACT_APP_API_KEY}&language=en-US`,
fetchUpcoming: `/movie/upcoming?api_key=${process.env.REACT_APP_API_KEY}&language=en-US`,
fetchQuery: (query) =>
`/search/movie?api_key=${process.env.REACT_APP_API_KEY}&language=en-US&query=${query}`,
fetchDetails: (id) =>
`/movie/${id}?api_key=${process.env.REACT_APP_API_KEY}&language=en-US`,
fetchVideos: (id) =>
`/movie/${id}/videos?api_key=${process.env.REACT_APP_API_KEY}&language=en-US`,
};
const imageBaseUrl = "https://image.tmdb.org/t/p/original/";
export const displayImage = (path) => `${imageBaseUrl}${path}`;
export default requests;
// src/components/Banner.js
import { useEffect, useState } from "react";
import { displayImage } from "../utils/requests";
import axios from "../utils/axios";
const Banner = ({ fetchUrl }) => {
const [movies, setMovies] = useState([]);
const [randomMovie, setRandomMovie] = useState(null);
useEffect(() => {
fetchData();
}, []);
async function fetchData() {
const response = await axios.get(fetchUrl);
setMovies(response.data.results);
}
useEffect(() => {
if (movies.length > 0) {
getRandomMovie();
const timer = setInterval(() => {
getRandomMovie();
}, 10 * 1000);
return () => clearInterval(timer);
}
}, [movies]);
function getRandomMovie() {
const randomMovie = movies[Math.floor(Math.random() * movies.length)];
setRandomMovie(randomMovie);
}
return randomMovie ? (
<div style=>
<div style=>
<h2>{randomMovie.title || randomMovie.name}</h2>
<p>{randomMovie.overview}</p>
</div>
<div style=>
<img
style=
src={displayImage(randomMovie.backdrop_path)}
alt={randomMovie.title || randomMovie.name}
/>
</div>
</div>
) : (
<h2>Loading...</h2>
);
};
export default Banner;
// src/components/Category.js
import { useEffect, useState } from "react";
import Movie from "./Movie";
import axios from "../utils/axios";
const Category = ({ title, fetchUrl }) => {
const [movies, setMovies] = useState([]);
useEffect(() => {
fetchData();
}, []);
async function fetchData() {
const response = await axios.get(fetchUrl);
setMovies(response.data.results);
}
return (
<section>
<h2>{title}</h2>
{movies.length > 0 ? (
<div style=>
{movies.map((movie) => (
<Movie
key={movie.id}
id={movie.id}
title={movie.title || movie.name}
poster_path={movie.poster_path}
/>
))}
</div>
) : (
<h3>Loading...</h3>
)}
</section>
);
};
export default Category;
// src/components/Movie.js
import { Link } from "react-router-dom";
import { displayImage } from "../utils/requests";
const Movie = (props) => {
return (
<Link to={`/movies/${props.id}`} style=>
<img
style=
src={displayImage(props.poster_path)}
alt={props.title}
/>
<h3>{props.title}</h3>
</Link>
);
};
export default Movie;
// src/components/Search.js
import { useState } from "react";
import axios from "../utils/axios";
import requests from "../utils/requests";
const Search = () => {
const [query, setQuery] = useState("");
const [searchResults, setSearchResults] = useState([]);
async function fetchSearchResults() {
try {
const response = await axios.get(requests.fetchQuery(query));
console.log(response);
setSearchResults(response.data.results);
} catch (error) {
console.error(error);
}
}
return (
<form
onSubmit={(e) => {
e.preventDefault();
fetchSearchResults();
}}
>
<input
type="text"
placeholder="Search"
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
<button type="submit">🔍</button>
</form>
);
};
export default Search;
// src/pages/Details.js
import { Component } from "react";
import { withRouter } from "react-router-dom";
import requests, { displayImage } from "../utils/requests";
import axios from "../utils/axios";
class Details extends Component {
constructor() {
super();
this.state = {
loading: true,
};
}
async componentDidMount() {
const movieResponse = await axios.get(
requests.fetchDetails(this.props.match.params.id)
);
this.setState(Object.assign({ ...this.state, movie: movieResponse.data }));
const trailerResponse = await axios.get(
requests.fetchVideos(this.props.match.params.id)
);
this.setState({
...this.state,
loading: false,
trailer: trailerResponse.data.results[0],
});
}
render() {
if (this.state.loading) {
return <h2>loading … </h2>;
}
const {
title,
name,
homepage,
genres,
overview,
release_date,
runtime,
backdrop_path,
} = this.state.movie;
const { key } = this.state.trailer;
return (
<section>
<div style=>
<div style=>
<h2>
<a href={homepage}>{title || name}</a>
</h2>
<ul>
{genres.map((genre) => (
<li key={genre.id}>{genre.name}</li>
))}
</ul>
<p>{overview}</p>
<div>Released: {release_date}</div>
<div>Runtime: {runtime}</div>
</div>
<div style=>
<img
style=
src={displayImage(backdrop_path)}
alt={title || name}
/>
</div>
</div>
<iframe
style=
title={this.state.trailer.name}
src={`https://www.youtube.com/embed/${key}`}
></iframe>
</section>
);
}
}
export default withRouter(Details);
// src/pages/Home.js
import Banner from "../components/Banner";
import Category from "../components/Category";
import requests from "../utils/requests";
const categories = [
{ title: "Now Playing", fetchUrl: requests.fetchNowPlaying },
{ title: "Popular", fetchUrl: requests.fetchPopular },
{ title: "Top Rated", fetchUrl: requests.fetchTopRated },
{ title: "Upcoming", fetchUrl: requests.fetchUpcoming },
];
const Home = () => {
return (
<div style=>
<Banner fetchUrl={requests.fetchTrending} />
{categories.map((category) => (
<Category
key={category.title}
title={category.title}
fetchUrl={category.fetchUrl}
/>
))}
</div>
);
};
export default Home;
// src/pages/Results.js
import { useLocation } from "react-router";
import Movie from "../components/Movie";
const Results = () => {
const {
state: { movies },
} = useLocation();
return (
<div style=>
{movies.map((movie) => (
<Movie
key={movie.id}
id={movie.id}
title={movie.title || movie.name}
poster_path={movie.poster_path}
/>
))}
</div>
);
};
export default Results;
// src/App.js
import { BrowserRouter as Router, Link, Route, Switch } from "react-router-dom";
import Details from "./pages/Details";
import Home from "./pages/Home";
import Results from "./pages/Results";
import Search from "./components/Search";
const App = () => {
return (
<Router>
<header
style=
>
<h1>
<Link to="/">React Workshop</Link>
</h1>
<Search />
</header>
<Switch>
<Route path="/movies/:id">
<Details />
</Route>
<Route path="/search">
<Results />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
</Router>
);
};
export default App;
// src/index.js
import ReactDOM from "react-dom";
import App from "./App";
ReactDOM.render(<App />, document.getElementById("root"));