---
# System prepended metadata

title: React project movie-app
tags: ['Javascript, React']

---

#  React project movie-app

###### tags: `Javascript, React`


主要操作 react 跟著課程手把手把這個 project 完成，使用到 useState, useEffect ，練習 fetch API 以及 render 到頁面上

# [成品網址](https://chiehliu.github.io/movie-app/)

## 示意圖
![](https://i.imgur.com/zSynULV.jpg)

# 功能:

* 可以搜尋電影
* 顯示電影大綱
* 顯示電影評分
* 有 default 圖片如果找不到的話

# JS 程式碼

## 使用的 API

* APIURL 主要使用來呈現 - 使用歡迎度來分類的第一頁的電影內容
* SEARCHAPI 則是可以在最後面的 query="" 字串串接上內容來搜尋資源
* IMGPATH 的部分就是呈現圖片

```javascript=
const APIURL =
  "https://api.themoviedb.org/3/discover/movie?sort_by=popularity.desc&api_key=04c35731a5ee918f014970082a0088b1&page=1";

const SEARCHAPI =
  "https://api.themoviedb.org/3/search/movie?&api_key=04c35731a5ee918f014970082a0088b1&query=";
  
const IMGPATH = "https://image.tmdb.org/t/p/w1280";
```
  

## App.js



```javascript=
import React, { useEffect, useState } from 'react';
import Movie from './components/Movie';

const APIURL =
  "https://api.themoviedb.org/3/discover/movie?sort_by=popularity.desc&api_key=04c35731a5ee918f014970082a0088b1&page=1";

const SEARCHAPI =
  "https://api.themoviedb.org/3/search/movie?&api_key=04c35731a5ee918f014970082a0088b1&query=";

function App() {

  const [movies, setMovies] = useState([]);
  const [searchTerm, setSearchTerm] = useState('');

  useEffect(() => {
    fetch(APIURL)
      .then(res => res.json())
      .then(data => {
        console.log(data);
        // 這邊是目前第一頁呈現的所有電影內容並且指派給 movies
        setMovies(data.results);
      })
  }, [])


// 主要處理 submit 後利用使用者輸入的內容加上搜尋API 重新 fetch 內容並且更新 state
  const handleOnSubmit = (e) => {
    e.preventDefault();

    if (searchTerm) {
      fetch(SEARCHAPI + searchTerm)
        .then(res => res.json())
        .then(data => {
          console.log(data);
          // 這邊把搜尋到的 data.results 更新到 movies，因此頁面呈現只剩下搜尋的內容
          setMovies(data.results);
          
        });
        
        // 當 movies 被更新後搜尋欄位則被清空
      setSearchTerm('');
    }

  }

// 這邊函式主要處理使用者在 search 輸入的內容要更新到 state searchTerm 裡面
  const handleOnChange = (e) => {
    setSearchTerm(e.target.value)
  }

  return (
    <div>
      <header>
      // 主要會在 form 表單加上 submit 事件監聽
        <form onSubmit={handleOnSubmit}>
          <input type="search" className="search" placeholder="Search..."
            value={searchTerm}
            onChange={handleOnChange}
          />
        </form>
      </header>
      <div className="movie-container">

        // 使用 map 印出內容傳到 <Movie> 內部當作 props 傳遞給 movie.js component
        {movies && movies.map((movie) => <Movie {...movie} key={movie.id} />
        )}
      </div>
    </div>
  );
}

export default App;
```

### 從 API 取得 Movie 的物件

* 取得物件(data)之後得從 json 轉回物件格式
* 接下來 setMovies 給 useState 中的 movies 使用

```javascript=
useEffect(() => {
    fetch(APIURL)
      .then(res => res.json())
      .then(data => {
        console.log(data);
        setMovies(data.results);
      })
  }, [])
```

### render 資料上去 DOM

成品會有兩個部分 Search, movie 

1. Search

* 針對整個 form 做 onSubmit 事件處理 並使用`handleOnSubmit`
在 submit 時再次執行 fetch 這次的 url 會加入 搜尋用url+searchTerm ，最後使用找到的資料更新 DOM(就會只剩搜尋到的內容)


* 對 value 的屬性做動態處理(上大括號)


* 針對整個 form 做 onChange 事件處理 並使用`handleOnChange`
會把目前使用者輸入在 input 的內容更新進去 searchTerm

2. movie

使用 `&&` 運算子判斷式 如果 movies 不為空就正常 render 出 movies 內容


```javascript=
return (
    <div>
      <header>
        <form onSubmit={handleOnSubmit}>
          <input type="search" className="search" placeholder="Search..."
            value={searchTerm}
            onChange={handleOnChange}
          />
        </form>
      </header>
      <div className="movie-container">

        {movies && movies.map((movie) => <Movie {...movie} key={movie.id} />
        )}
      </div>
    </div>
  );
```

## Movie.js

當作子 component ，接收來自 App.js 的 props 使用解構的方式提取 results 的內容 poster_path, title, overview, vote_average 來呈現到瀏覽器上

```javascript=
const IMGPATH = "https://image.tmdb.org/t/p/w1280";

const Movie = ({ poster_path, title, overview, vote_average }) => {

    const setVoteClass = (vote) => {
        if (vote >= 8) {
            return 'green'
        } else if (vote >= 6) {
            return 'orange'
        } else {
            return "red"
        }
    }

    return (
        <div className="movie">
            <img src={poster_path ? (IMGPATH + poster_path) : 'https://images.unsplash.com/photo-1485846234645-a62644f84728?ixlib-rb-1.2.1&ixid=eyjhcHBfawQiOjEyMDd9&auto=format&fit=crop&w=1480&q=80'} alt={title} />
            <div className="movie-info">
                <h3>{title}</h3>
                <span className={`tag ${setVoteClass(vote_average)}`}>{vote_average}</span>
            </div>

            <div className="movie-over">
                <h2>Overview:</h2>
                <p>{overview}</p>
            </div>
        </div>
    );
}

export default Movie;
```


### 預設圖片設置(如果電影圖片沒有正常顯示的話)

使用三元運算子操作 poster_path 是否為 true，如果為真則正常顯示`IMGPATH + poster_path` 為 false 的話顯示一張圖片的路徑當作預設圖片

```javascript=
<img src={poster_path ? (IMGPATH + poster_path) : 'https://images.unsplash.com/photo-1485846234645-a62644f84728?ixlib-rb-1.2.1&ixid=eyjhcHBfawQiOjEyMDd9&auto=format&fit=crop&w=1480&q=80'} alt={title} />
```


### 判斷式

處理顯示數字的顏色，針對 className 做函式操作添加的 class tag

