ホームページ >ウェブフロントエンド >CSSチュートリアル >React を使用して Movie Finder Web サイトを構築する

React を使用して Movie Finder Web サイトを構築する

DDD
DDDオリジナル
2024-09-13 12:39:34625ブラウズ

Building a Movie Finder Website using React

導入

このブログでは、React と OMDB API を使用して Movie Finder Web サイトを構築するプロセスを順を追って説明します。この Web サイトでは、ユーザーはアベンジャーズ、スター ウォーズ、シリーズなどのカテゴリ別に映画を閲覧したり、特定のクエリを使用して映画を検索したりできます。各映画には詳細ページがあり、お気に入りの映画について簡単に詳しく調べることができます。

プロジェクト概要

Movie Finder Web サイト では、ユーザーは次のことが可能になります:

  • アベンジャーズやスター ウォーズなどのカテゴリを参照します。
  • キーワードで映画を検索します。
  • 映画の詳細情報 (ポスター、ジャンル、監督、俳優など) を表示します。
  • すっきりとしたモダンなデザインで、ウェブサイト内を簡単に移動できます。

特徴

  • 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. ホームページ

ホームページでは、アベンジャーズとスター・ウォーズという 2 つのカテゴリの映画が紹介されています。ユーザーがムービー カードをクリックすると、映画の詳細ページにリダイレクトされます。

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 ? (
      391706c98a6bb525cf4d3f804c3e9b3baac6cad6c09b9658b5b35f3523d88d1116b28748ea4df4d9c2150843fecfba6816b28748ea4df4d9c2150843fecfba68
    ) : (
      708b1d1285701e251e9fa1f2193b7d63
        {movies.length > 0 ? (
          movies.map((movie) => (
            12728e1adecf83573e180dc578e2e2c2 handleCardClick(movie.imdbID)}>
              53f77bfc03dd61d765abffefdb992f4f
              c1a436a314ed609750bd7c7d319db4da{movie.Title}2e9b454fa8428549ca2e64dfac4625cd
            16b28748ea4df4d9c2150843fecfba68
          ))
        ) : (
          e388a4556c0f65e1904146cc1a846beeNo movies found.94b3e26ee717c64999d7867364b1b4a3
        )}
      16b28748ea4df4d9c2150843fecfba68
    )
  );

  return (
    a8093152e673feb7aba1828c43532094
      5e12fe6a884c5486b6a8efaa94283304
        9b0bcc9b98322501402178b0f3be9a1e
          3f7b3decd2dcafb07b84d2d3985d9f40Avengers Movies0f6dfd1e3624ce5465eb402e300e01ae
          {renderMovies(avengersMovies, loadingAvengers)}
        16b28748ea4df4d9c2150843fecfba68
        df250b2156c434f3390392d09b1c9563
        df250b2156c434f3390392d09b1c9563
        9b0bcc9b98322501402178b0f3be9a1e
          3f7b3decd2dcafb07b84d2d3985d9f40Star Wars Movies0f6dfd1e3624ce5465eb402e300e01ae
          {renderMovies(starWarsMovies, loadingStarWars)}
        16b28748ea4df4d9c2150843fecfba68
      16b28748ea4df4d9c2150843fecfba68
      52f3849cf2c3f31cc82a4e8b32ced1d3
      ecdc054e702196f465a773413a599e82
    5f557f62ae7ac7a14e0b1cb564790dfc
  );
};

export default Home;

2. 検索機能

ユーザーは、Web サイトの上部にある検索バーを使用して映画を検索できます。検索結果は、ユーザーのクエリに基づいて 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 (
    bf6ea6bca350fbd36c62942eeadc3f36
      3f7b3decd2dcafb07b84d2d3985d9f40Search Results for "{query}"0f6dfd1e3624ce5465eb402e300e01ae
      {loading ? (
        391706c98a6bb525cf4d3f804c3e9b3baac6cad6c09b9658b5b35f3523d88d1116b28748ea4df4d9c2150843fecfba6816b28748ea4df4d9c2150843fecfba68  // Loader
      ) : (
        708b1d1285701e251e9fa1f2193b7d63
          {movies.length > 0 ? (
            movies.map((movie) => (
              12728e1adecf83573e180dc578e2e2c2 handleCardClick(movie.imdbID)}>
                53f77bfc03dd61d765abffefdb992f4f
                c1a436a314ed609750bd7c7d319db4da{movie.Title}2e9b454fa8428549ca2e64dfac4625cd
              16b28748ea4df4d9c2150843fecfba68
            ))
          ) : (
            e388a4556c0f65e1904146cc1a846beeNo results found.94b3e26ee717c64999d7867364b1b4a3
          )}
        16b28748ea4df4d9c2150843fecfba68
      )}
    16b28748ea4df4d9c2150843fecfba68
  );
};

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 391706c98a6bb525cf4d3f804c3e9b3b
    aac6cad6c09b9658b5b35f3523d88d1116b28748ea4df4d9c2150843fecfba68
  16b28748ea4df4d9c2150843fecfba68;
  if (!movie) return bc3517ed53a2992e46268da16e3f760bNo movie data found!16b28748ea4df4d9c2150843fecfba68;

  return (
    5d4fb5b3e2c1bf52ca40540d9f64ecf6

7d32ff64584c41174c506c75fd1c43f8
4a249f0d628e2318394fd9b75b4636b1{movie.Title}473f0a7621bec819994bb5020d29372a
e388a4556c0f65e1904146cc1a846bee8e99a69fbe029cd4e2b854e244eab143Year:128dba7a3a77be0113eb0bea6ea0a5d0 {movie.Year}94b3e26ee717c64999d7867364b1b4a3
      e388a4556c0f65e1904146cc1a846bee8e99a69fbe029cd4e2b854e244eab143Rating:128dba7a3a77be0113eb0bea6ea0a5d0 {movie.imdbRating}94b3e26ee717c64999d7867364b1b4a3
      e388a4556c0f65e1904146cc1a846bee8e99a69fbe029cd4e2b854e244eab143Genre:128dba7a3a77be0113eb0bea6ea0a5d0 {movie.Genre}94b3e26ee717c64999d7867364b1b4a3
      e388a4556c0f65e1904146cc1a846bee8e99a69fbe029cd4e2b854e244eab143Director:128dba7a3a77be0113eb0bea6ea0a5d0 {movie.Director}94b3e26ee717c64999d7867364b1b4a3
      e388a4556c0f65e1904146cc1a846bee8e99a69fbe029cd4e2b854e244eab143Actors:128dba7a3a77be0113eb0bea6ea0a5d0 {movie.Actors}94b3e26ee717c64999d7867364b1b4a3
      e388a4556c0f65e1904146cc1a846bee8e99a69fbe029cd4e2b854e244eab143Plot:128dba7a3a77be0113eb0bea6ea0a5d0 {movie.Plot}94b3e26ee717c64999d7867364b1b4a3
      e388a4556c0f65e1904146cc1a846bee8e99a69fbe029cd4e2b854e244eab143Runtime:128dba7a3a77be0113eb0bea6ea0a5d0 {movie.Runtime}94b3e26ee717c64999d7867364b1b4a3
      e388a4556c0f65e1904146cc1a846bee8e99a69fbe029cd4e2b854e244eab143Language:128dba7a3a77be0113eb0bea6ea0a5d0 {movie.Language}94b3e26ee717c64999d7867364b1b4a3
      e388a4556c0f65e1904146cc1a846bee8e99a69fbe029cd4e2b854e244eab143Country:128dba7a3a77be0113eb0bea6ea0a5d0 {movie.Country}94b3e26ee717c64999d7867364b1b4a3
      e388a4556c0f65e1904146cc1a846bee8e99a69fbe029cd4e2b854e244eab143Awards:128dba7a3a77be0113eb0bea6ea0a5d0 {movie.Awards}94b3e26ee717c64999d7867364b1b4a3
16b28748ea4df4d9c2150843fecfba68
    bcf6b949990d2b26854fc1a0235e7896
    53f77bfc03dd61d765abffefdb992f4f
    16b28748ea4df4d9c2150843fecfba68
    16b28748ea4df4d9c2150843fecfba68
  );
};

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 (
    d40b792c730e4d11d0eca74a10366575
      c1a436a314ed609750bd7c7d319db4daMovies2e9b454fa8428549ca2e64dfac4625cd
      708b1d1285701e251e9fa1f2193b7d63
        {movies.map(movie => (
          12728e1adecf83573e180dc578e2e2c2 handleCardClick(movie.imdbID)}>
            53f77bfc03dd61d765abffefdb992f4f
            684271ed9684bde649abda8831d4d355{movie.Title}39528cedfa926ea0c01e69ef5b2ea9b0
          16b28748ea4df4d9c2150843fecfba68
        ))}
      16b28748ea4df4d9c2150843fecfba68
    16b28748ea4df4d9c2150843fecfba68
  );
};

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 (
    31838116f0659c8e0fe2d7d1854d0e7a
      c1a436a314ed609750bd7c7d319db4daTV Series2e9b454fa8428549ca2e64dfac4625cd
      708b1d1285701e251e9fa1f2193b7d63
        {series.map(show => (
          01c7d619b10fabc0744128b826b29a0f handleCardClick(show.imdbID)}>
            a9c0cac5f8f3bc0104ebd6d33bb9bd98
            684271ed9684bde649abda8831d4d355{show.Title}39528cedfa926ea0c01e69ef5b2ea9b0
          16b28748ea4df4d9c2150843fecfba68
        ))}
      16b28748ea4df4d9c2150843fecfba68
    16b28748ea4df4d9c2150843fecfba68
  );
};

export default Series;

5. ナビゲーションバーコンポーネント

Navbar コンポーネントを使用すると、ユーザーはさまざまなページ間を移動し、検索を実行できます。

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 (
    6de5cdabed8c37e65ec07ad5a9b8f878
      788a37ae874cfc6a3ed79faf72b77879
        4a249f0d628e2318394fd9b75b4636b1Movie Finder473f0a7621bec819994bb5020d29372a
      16b28748ea4df4d9c2150843fecfba68

      48f38cae923f5e26c602cafad0cbf526
        7a33e57c3b200f2f5ecc2e455318e8cf
          3f7b3decd2dcafb07b84d2d3985d9f40Home0f6dfd1e3624ce5465eb402e300e01ae
        adb9ac3aead1d3fba02ca688b8636d1b
        652dfcca303e1ed15c65622e15127ba2
          3f7b3decd2dcafb07b84d2d3985d9f40Movies0f6dfd1e3624ce5465eb402e300e01ae
        adb9ac3aead1d3fba02ca688b8636d1b
        d63227fa461236d17a6381d446ab43a0
          3f7b3decd2dcafb07b84d2d3985d9f40TV Series0f6dfd1e3624ce5465eb402e300e01ae
        adb9ac3aead1d3fba02ca688b8636d1b
      16b28748ea4df4d9c2150843fecfba68
      a349cf54cf26717db21be5f2029cbdd5
        a09ba7022bc6a6807a1896f2a858385a setSearchQuery(e.target.value)}
          onKeyDown={handleSearch}
        />
        e4f7f070cfb179e9f270abf5ddd9c301
          bb9345e55eb71822850ff156dfde57c8Search65281c5ac262bf6d81768915a4a77ac0
        06f735b502bd5273dad825215f7c405b
      16b28748ea4df4d9c2150843fecfba68
    16b28748ea4df4d9c2150843fecfba68
  );
};

export default Navbar;

6. フッターコンポーネント

フッター コンポーネントは、単純なフッター メッセージを提供します。

フッター.js

import React from 'react';

const Footer = () => {
  return (
    c36ce888baa3a5bd9bc4a4919fc19de1
      Made with 45a2772a6b6107b401db3c9b82c049c2❤️54bdf357c58b8a65c66d7c19c8e4d114 by Abhishek Gurjar
    16b28748ea4df4d9c2150843fecfba68
  );
};

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: 9c7dadb03dddabc8a9ba04a02322601d16b28748ea4df4d9c2150843fecfba68 */
.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 Web サイトのライブデモをチェックできます。

結論

このブログでは、React、React Router、Axios を使用して Movie Finder Web サイトを作成する方法を学びました。このプロジェクトでは、パブリック API と対話し、React で状態を管理し、シンプルでありながら機能的なムービー閲覧エクスペリエンスを作成する方法を示します。

自由にデザインをカスタマイズし、ユーザー レビューや映画の評価などの機能を追加して、よりダイナミックなものにしてください。


クレジット

  • OMDB API
  • 反応
  • 反応ルーター

著者

Abhishek Gurjar は、実用的で機能的な Web アプリケーションの作成に情熱を注ぐ専任の Web 開発者です。 GitHub で彼のプロジェクトをさらにチェックしてください。

以上がReact を使用して Movie Finder Web サイトを構築するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。