PHP前端开发

使用 React 构建电影查找网站

百变鹏仔 4个月前 (09-19) #CSS
文章标签 电影

介绍

在本博客中,我们将逐步介绍使用 react 和 omdb api 构建 movie finder 网站的过程。该网站允许用户按复仇者联盟、星球大战和系列等类别浏览电影,并使用特定查询搜索电影。每部电影都有其详细页面,让您轻松探索更多有关您喜爱的电影。

项目概况

电影查找网站使用户能够:

特征

使用的技术

项目结构

这是该项目的目录结构:

movie-finder/├── public/├── src/│   ├── components/│   │   └── navbar.js│   │   └── footer.js│   ├── pages/│   │   └── home.js│   │   └── movies.js│   │   └── series.js│   │   └── searchresults.js│   │   └── moviedetail.js│   └── app.js│   └── app.css└── package.json

安装

  1. 克隆存储库:

    git clone https://github.com/abhishekgurjar-in/movie-finder.gitcd movie-finder
  2. 安装依赖项:

    npm install
  3. 从 omdb api 获取您的 api 密钥。

  4. 在项目根目录中创建一个 .env 文件并添加您的 api 密钥:

    react_app_omdb_api_key=yourapikey
  5. 运行项目:

    npm start

用法

1. 主页

主页展示了两类电影:《复仇者联盟》和《星球大战》。当用户点击电影卡时,他们会被重定向到详细的电影页面。

来自 home.js 的代码片段:

import react, { useeffect, usestate } from "react";import axios from "axios";import { usenavigate } from "react-router-dom";import movies from "./movies";import series from "./series";const home = () => {  const [avengersmovies, setavengersmovies] = usestate([]);  const [starwarsmovies, setstarwarsmovies] = usestate([]);  const [loadingavengers, setloadingavengers] = usestate(true);  const [loadingstarwars, setloadingstarwars] = usestate(true);  const navigate = usenavigate();  useeffect(() => {    fetchmovies("avengers", setavengersmovies, setloadingavengers);    fetchmovies("star wars", setstarwarsmovies, setloadingstarwars);  }, []);  const fetchmovies = (query, setmovies, setloading) => {    axios      .get(`http://www.omdbapi.com/?s=${query}&apikey=you_api_key`)      .then((response) => {        if (response.data.search) {          setmovies(response.data.search);        } else {          setmovies([]); // clear movies if no results        }      })      .catch((error) => {        console.error(`there was an error fetching the ${query} movies!`, error);        setmovies([]); // clear movies if there is an error      })      .finally(() => {        setloading(false);      });  };  const handlecardclick = (id) => {    navigate(`/movie/${id}`);  };  const rendermovies = (movies, loading) => (    loading ? (      <div classname="loader"><div classname="load"></div></div>    ) : (      <div classname="cards">        {movies.length > 0 ? (          movies.map((movie) => (            <div key={movie.imdbid} classname="card" onclick={() => handlecardclick(movie.imdbid)}>              @@##@@              <h2>{movie.title}</h2>            </div>          ))        ) : (          <p>no movies found.</p>        )}      </div>    )  );  return (    <>      <div classname="home">        <div classname="movie-category">          <h4>avengers movies</h4>          {rendermovies(avengersmovies, loadingavengers)}        </div>        <br />        <br />        <div classname="movie-category">          <h4>star wars movies</h4>          {rendermovies(starwarsmovies, loadingstarwars)}        </div>      </div>      <movies />      <series />    </>  );};export default home;

2. 搜索功能

用户可以使用网站顶部的搜索栏搜索任何电影。搜索结果是根据用户的查询从 omdb api 获取的。

来自 searchresults.js 的代码片段:

import react, { useeffect, usestate } from "react";import axios from "axios";import { useparams, usenavigate } from "react-router-dom";const searchresults = () => {  const [movies, setmovies] = usestate([]);  const [loading, setloading] = usestate(false);  const { query } = useparams();  const navigate = usenavigate(); // add this line to use navigate  useeffect(() => {    if (query) {      setloading(true);  // set loading to true before starting the fetch      axios        .get(`http://www.omdbapi.com/?s=${query}&apikey=your_api_key`)        .then((response) => {          if (response.data.search) {            setmovies(response.data.search);          } else {            setmovies([]); // clear movies if no results          }        })        .catch((error) => {          console.error("there was an error fetching the movie data!", error);        })        .finally(() => {          setloading(false);  // set loading to false once fetch is complete        });    }  }, [query]);  const handlecardclick = (id) => {    navigate(`/movie/${id}`); // navigate to movie detail page  };  return (    <div classname="search-results">      <h4>search results for "{query}"</h4>      {loading ? (        <div classname="loader"><div classname="load"></div></div>  // loader      ) : (        <div classname="cards">          {movies.length > 0 ? (            movies.map((movie) => (              <div key={movie.imdbid} classname="card" onclick={() => handlecardclick(movie.imdbid)}>                @@##@@                <h2>{movie.title}</h2>              </div>            ))          ) : (            <p>no results found.</p>          )}        </div>      )}    </div>  );};export default searchresults;

3. 电影详情页

当用户点击电影时,他们会被重定向到电影详细信息页面。此页面显示电影的标题、海报、情节、演员等。

来自 moviedetail.js 的代码片段:

import react, { useeffect, usestate } from 'react';import axios from 'axios';import { useparams } from 'react-router-dom';const moviedetail = () => {  const [movie, setmovie] = usestate(null);  const [loading, setloading] = usestate(true);  const { id } = useparams();  useeffect(() => {    axios.get(`http://www.omdbapi.com/?i=${id}&apikey=your_api_key`)      .then((response) => {        setmovie(response.data);      })      .catch((error) => {        console.error("there was an error fetching the movie details!", error);      })      .finally(() => {        setloading(false);      });  }, [id]);  if (loading) return <div classname="loader">    <div classname="load"></div>  </div>;  if (!movie) return <div classname='loader'>no movie data found!</div>;  return (    <div classname="movie-detail"><div classname="detail-box"><h1>{movie.title}</h1><p><strong>year:</strong> {movie.year}</p>      <p><strong>rating:</strong> {movie.imdbrating}</p>      <p><strong>genre:</strong> {movie.genre}</p>      <p><strong>director:</strong> {movie.director}</p>      <p><strong>actors:</strong> {movie.actors}</p>      <p><strong>plot:</strong> {movie.plot}</p>      <p><strong>runtime:</strong> {movie.runtime}</p>      <p><strong>language:</strong> {movie.language}</p>      <p><strong>country:</strong> {movie.country}</p>      <p><strong>awards:</strong> {movie.awards}</p></div>    <div classname="img-box">    @@##@@    </div>    </div>  );};export default moviedetail;

4. 电影和连续剧页面

movies.jsseries.js 页面分别列出电影和电视剧。

来自 movies.js 的代码片段:

import react, { useeffect, usestate } from "react";import axios from "axios";import { usenavigate } from "react-router-dom";const movies = () => {  const [movies, setmovies] = usestate([]);  const navigate = usenavigate();  useeffect(() => {    axios      .get(`http://www.omdbapi.com/?s=avengers&type=movie&apikey=${process.env.react_app_omdb_api_key}`)      .then(response => setmovies(response.data.search || []))      .catch(error => console.error(error));  }, []);  const handlecardclick = (id) => {    navigate(`/detail/${id}`);  };  return (    <div classname="movies">      <h2>movies</h2>      <div classname="cards">        {movies.map(movie => (          <div key={movie.imdbid} classname="card" onclick={() => handlecardclick(movie.imdbid)}>            @@##@@            <h3>{movie.title}</h3>          </div>        ))}      </div>    </div>  );};export default movies;

series.js 中的代码片段:

import react, { useeffect, usestate } from "react";import axios from "axios";import { usenavigate } from "react-router-dom";const series = () => {  const [series, setseries] = usestate([]);  const navigate = usenavigate();  useeffect(() => {    axios      .get(`http://www.omdbapi.com/?s=star wars&type=series&apikey=${process.env.react_app_omdb_api_key}`)      .then(response => setseries(response.data.search || []))      .catch(error => console.error(error));  }, []);  const handlecardclick = (id) => {    navigate(`/detail/${id}`);  };  return (    <div classname="series">      <h2>tv series</h2>      <div classname="cards">        {series.map(show => (          <div key={show.imdbid} classname="card" onclick={() => handlecardclick(show.imdbid)}>            @@##@@            <h3>{show.title}</h3>          </div>        ))}      </div>    </div>  );};export default series;

5. 导航栏组件

导航栏组件允许用户在不同页面之间导航并执行搜索。

更新了 navbar.js

import react, { usestate } from "react";import { navlink, link } from "react-router-dom";const navbar = () => {  const [searchquery, setsearchquery] = usestate("");  const handlesearch = (event) => {    if (event.key === 'enter' && searchquery.trim()) {      document.getelementbyid('search-link').click();    }  };  return (    <div classname="navbar">      <div classname="logo">        <h1>movie finder</h1>      </div>      <div classname="page-list">        <navlink to="/">          <h4>home</h4>        </navlink>        <navlink to="/movies">          <h4>movies</h4>        </navlink>        <navlink to="/series">          <h4>tv series</h4>        </navlink>      </div>      <div classname="search-box">        <input          type="text"          placeholder="search for movies or series"          value={searchquery}          onchange={(e) => setsearchquery(e.target.value)}          onkeydown={handlesearch}        />        <link to={`/search/${searchquery}`} id="search-link">          <button>search</button>        </link>      </div>    </div>  );};export default navbar;

6.页脚组件

页脚组件提供简单的页脚消息。

页脚.js

import react from 'react';const footer = () => {  return (    <div classname='footer'>      made with <span>❤️</span> by abhishek gurjar    </div>  );};export default footer;
*{  box-sizing: border-box;}body{  font-family: sans-serif;  margin: 0;  padding: 0;}.navbar {  padding-inline: 100px;  display: flex;  align-items: center;  justify-content: space-between;  background-color: red;}.search-btn{  background-color: red;}.logo h1{  font-size: 25px;  color:black;  }.page-list {  display: flex;  align-items: center;  gap: 40px;}.page-list a{  color: white;  text-decoration: none;  font-size: 20px;}.page-list a:hover{color: black;}.page-list a.active{  color: black;}.search-box{  box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px;  background-color:white;  color: gray;  width: 250px;  height: 40px;  border-radius: 50px;  overflow: hidden;}.search-box input{  width: 200px;  height: 40px;  margin-left: 10px;  border: none;  outline: none;}.home{  margin-block: 40px;  margin-inline: 60px;}.home h4{  font-size: 16px;}.movies{  margin-block: 40px;  margin-inline: 60px;}.movies h4{  font-size: 16px;}.cards{  display: flex;flex-wrap: wrap;  align-items:center ;  justify-content: space-between;  gap: 10px;}.card{  width:200px;  height:360px;  border-radius: 10px;  overflow: hidden;  box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px;}.card img{  width: 200px;  height: 290px;  object-fit: cover;}.card h2{  margin: 10px;  font-size: 16px;text-align: center;}.series{  margin-block: 40px;  margin-inline: 60px;}.series h4{  font-size: 16px;}.home{  margin-block: 40px;  margin-inline: 60px;}.search-results{  margin-block: 40px;  margin-inline: 60px;}.search-results h4{  font-size: 16px;}.loader{  min-height: 90vh;  display: flex;  align-items: center;  justify-content: center;}/* HTML: <div class="loader"></div> */.load {  width: 50px;  padding: 8px;  aspect-ratio: 1;  border-radius: 50%;  background: #ff1900;  --_m:     conic-gradient(#0000 10%,#000),    linear-gradient(#000 0 0) content-box;  -webkit-mask: var(--_m);          mask: var(--_m);  -webkit-mask-composite: source-out;          mask-composite: subtract;  animation: l3 1s infinite linear;}@keyframes l3 {to{transform: rotate(1turn)}}.movie-detail {  margin-block: 40px;  margin-inline: 60px;  display: flex;  align-items: flex-start;  justify-content: space-between;}img-box{  width: 50%;}.movie-detail img {  border-radius: 10px;width: 330px; height: auto; object-fit: cover; box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px;}.detail-box{  width: 50%;}.movie-detail p {  font-size: 18px;  margin: 10px 0;}.movie-detail a {  display: inline-block;  margin-top: 20px;  color: #007bff;  text-decoration: none;}.movie-detail a:hover {  text-decoration: underline;}.footer{  width: 100%;  background-color: red;  text-align: center;  color: white;  padding: 20px;}

现场演示

您可以在此处查看 movie finder 网站的现场演示。

结论

在本博客中,我们学习了如何使用 react、react router 和 axios 创建 movie finder 网站。该项目演示了如何与公共 api 交互、在 react 中管理状态以及创建简单而实用的电影浏览体验。

随意定制设计并添加更多功能,例如用户评论或电影评分,使其更加动态!


制作人员

作者

abhishek gurjar 是一位专注的 web 开发人员,热衷于创建实用且功能性的 web 应用程序。在 github 上查看他的更多项目。