在本部落格中,我們將逐步介紹使用 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
複製儲存庫:
git clone https://github.com/abhishekgurjar-in/movie-finder.git cd movie-finder
安裝依賴項:
npm install
從 OMDB API 取得您的 API 金鑰。
在專案根目錄中建立一個 .env 檔案並新增您的 API 金鑰:
REACT_APP_OMDB_API_KEY=yourapikey
運行項目:
npm start
首頁展示了兩類電影:《復仇者聯盟》和《星際大戰》。當用戶點擊電影卡時,他們會被重新導向到詳細的電影頁面。
來自 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;
使用者可以使用網站頂部的搜尋欄來搜尋任何電影。搜尋結果是根據使用者的查詢從 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;
當使用者點擊電影時,他們會被重新導向到電影詳細資料頁面。此頁面顯示電影的標題、海報、情節、演員等。
來自 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;
Movies.js 和 Series.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;
導覽列元件允許使用者在不同頁面之間導航並執行搜尋。
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;
頁腳組件提供簡單的頁腳訊息。
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 上查看他的更多專案。
以上是使用 React 建立電影尋找網站的詳細內容。更多資訊請關注PHP中文網其他相關文章!