首页 > web前端 > css教程 > 使用 React 构建电影查找网站

使用 React 构建电影查找网站

DDD
发布: 2024-09-13 12:39:34
原创
614 人浏览过

Building a Movie Finder Website using React

介绍

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

项目概况

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

  • 浏览复仇者联盟和星球大战等类别。
  • 通过关键字搜索电影。
  • 查看详细的电影信息(海报、类型、导演、演员等)。
  • 通过简洁、现代的设计轻松浏览网站。

特征

  • 使用 OMDB API 动态获取数据。
  • 响应式设计,提供更好的用户体验。
  • 提供即时结果的搜索功能。
  • 在获取数据时加载指标。
  • 在单独的页面上查看各个电影的详细信息。

使用的技术

  • React:用于构建 UI 组件的前端库。
  • React Router:用于导航和路由。
  • Axios:用于向 OMDB API 发出 HTTP 请求。
  • OMDB API:获取电影详细信息。
  • CSS:用于设计应用程序的样式。

项目结构

这是该项目的目录结构:

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.git
    cd 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)}>
              <img src={movie.Poster} alt={movie.Title} />
              <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)}>
                <img src={movie.Poster} alt={movie.Title} />
                <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">
    <img src={movie.Poster} alt={movie.Title} />
    </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)}>
            <img src={movie.Poster} alt={movie.Title} />
            <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)}>
            <img src={show.Poster} alt={show.Title} />
            <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 中管理状态以及创建简单而实用的电影浏览体验。

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


制作人员

  • OMDB API
  • 反应
  • 反应路由器

作者

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

以上是使用 React 构建电影查找网站的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板