首頁 > web前端 > css教學 > 使用 React 建立電影尋找網站

使用 React 建立電影尋找網站

DDD
發布: 2024-09-13 12:39:34
原創
586 人瀏覽過

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
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板