React+Youtube APIでデータを取得して動画を埋め込む

## 概要
YouTube Data API(v3)で検索からYoutubeデータ取得し、iframeで動画を埋め込みたい。

公式リファレンス
・[YouTube Data API(v3)](https://developers.google.com/youtube/v3/docs/search?hl=ja)
・[IFrame Player API
](https://developers.google.com/youtube/iframe_api_reference?hl=ja)

## 前提
・node.jsとnpm
・Youtube APIのアクセスkey

[Youtube Data API Key の取得手順
](http://piyohiko.webcrow.jp/kids_tube/help/index.html)

## 準備

create-react-appで始めていきます。

```
$ create-react-app project
$ cd project
$ npm start
```
public/srcにcomponentsディレクトリを追加して、下記の構成にする(各ディレクトリ以下はindex.js)

```
components
  ├── Header
  └── Youtube
```

## Youtube APIからデータ取得

初めにAPIからデータをaxiosを使って取得します。

axiosのインストール

```
$ npm install axios --save
```

App.jsを編集して、とりあえず以下の条件で取得できるか試してみます。
・ 検索ワード: Create-React-APP
・ 検索数は: ３

期待する検索結果のJSON構造はこの形式

```javascript

{
  "kind": "youtube#searchResult",
  "etag": etag,
  "id": {
    "kind": string,
    "videoId": string,
    "channelId": string,
    "playlistId": string
  },
  "snippet": {
    "publishedAt": datetime,
    "channelId": string,
    "title": string,
    "description": string,
    "thumbnails": {
      (key): {
        "url": string,
        "width": unsigned integer,
        "height": unsigned integer
      }
    },
    "channelTitle": string
  }
}
```
App.js編集

```react:src/App.js

import React from 'react';
import axios from 'axios';

const YOUTUBE_API_KEY = process.env.REACT_APP_YOUTUBE_API_KEY;

export default class App extends React.Component {
  state = {
    videos: [],
    keyword: 'Create-React-APP'
  }

  componentDidMount() {
    const url = `https://www.googleapis.com/youtube/v3/search?type=video&part=snippet&q=${this.state.keyword}&maxResults=3&key=${YOUTUBE_API_KEY}`;

    axios
      .get(url)
      .then(response => {
          this.setState({
            videos: response.data.items,
          });
      })
      .catch(() => {
          console.log('通信に失敗しました');
      });
  }

  render() {
    console.log(this.state.videos)
    return (
      <>
      </>
    )
  }
}
```


create-react-appでは、gitignoreにデフォルトでenv.localが入っているので.env.localを作成してここにアクセスキーを記述する。

REACT_APPを前につける必要があり、JS側では記述したKeyの名前の先頭にprocess.envをつけることで取得できる

```:.env.local
# API
REACT_APP_YOUTUBE_API_KEY = '自分のアクセスキー'
```

反映する為に立ち上げ直す

```
$ npm start
```

コンソールを確認するとこのようになり
![スクリーンショット 2019-07-20 18.17.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379873/8c4aca09-5301-a493-3b63-04086fe290ee.png)

条件通り取得できています

## 検索フォームの作成

では、検索フォームで入力したキーワードから取得できるようにしていきます。

再度App.jsの修正します。
APIのパラメータに検索フォームの値を使いたいのでのonSerchYoutube関数を記述し、値を受け取る為Header/index.jsにpropsとして渡します。

```react:src/App.js
import React from 'react';
import axios from 'axios';
import Header from './components/Header';

const YOUTUBE_API_KEY = process.env.REACT_APP_YOUTUBE_API_KEY;

export default class App extends React.Component {
  state = {
    videos: [],
  }

  onSerchYoutube = (keyword) => {
    const url = `https://www.googleapis.com/youtube/v3/search?type=video&part=snippet&q=${keyword}&maxResults=3&key=${YOUTUBE_API_KEY}`;

    axios
      .get(url)
      .then(response => {
          this.setState({
            videos: response.data.items,
          });
      })
      .catch(() => {
          console.log('通信に失敗しました');
      });
  }

  render() {
    return (
      <>
        <Header onSerchYoutube={this.onSerchYoutube} />
      </>
    )
  }
}
```


Header/index.jsに検索フォームを置いていきます。

YouTube APIでは[クォータの使用量](https://developers.google.com/youtube/v3/getting-started?hl=ja)が決まっており、使用量を考えなければ、一日の上限を超え制限がかかる恐れが有ります。

検索フォームでは、onChangeする度にAPIを呼び出されてしまうので、lodashのdebounceを活用し制限します。


lodashインストール

```
$ npm install --save lodash
```
debounceは更新中は関数を呼び出さずに、任意の時間内で更新されなかった時、関数を実行するようにできます。今回は0.2秒の待ち時間を与えます。

参考: https://qiita.com/waterada/items/986660d31bc107dbd91c

```react:Header/index.js
import React from 'react';
import _ from 'lodash';

export default class Header extends React.Component {

    state = {
        keyword: "",
    };
    
    handleChangeInput = (e) => {
        this.setState({keyword: e.target.value});
        this._debounce(e.target.value)
    }

    _debounce = _.debounce(value => {
        this.props.onSerchYoutube(value);
    },200);

    render() {
        return (
            <div style={{marginTop: '20px', textAlign:'center'}}>
                <input 
                  onChange={this.handleChangeInput}
                  value={this.state.keyword}
                />
            </div>
        )
    }
}
```
これで検索ワードからYoutubeのデータが取得できました。

## YouTubeを埋め込む

データからYouTubeを埋め込んでいきます。

Youtube/index.jsにstateのvideos渡すようにApp.jsを編集。

```react:App.js
// 追加
import Youtube from './components/Youtube';
...

render() {
    return (
      <>
        <Header onSerchYoutube={this.onSerchYoutube} />
        {/* 追加 */}
        <Youtube videos={this.state.videos}/>
      </>
    )
  }
```


APIリファレンス[IFrame Player API](https://developers.google.com/youtube/iframe_api_reference?hl=ja)から

```html
<iframe id="ytplayer" type="text/html" width="640" height="360"
  src="http://www.youtube.com/embed/M7lc1UVf-VE?autoplay=1&origin=http://example.com"
  frameborder="0"/>
```

iframeタグのurlにpropsをmapした各videoのvideoIdを入れる。

Youtube/index.jsの作成

```react:Youtube/index.js
import React from 'react';

const Youtube = (props) => {

    const video = props.videos.map((video) => {
        const url = 'https://www.youtube.com/embed/' + video.id.videoId;

        return ( 
            <div style={{margin: '20px', textAlign: 'center'}}>
                <iframe 
                  id="ytplayer" 
                  type="ytplayer" 
                  width="480" 
                  height="270"
                  src={url}
                  frameborder="0"
                />
            </div>
        )
    });

    return (
        <div style={{marginTop: '10px'}}>
          {video}
        </div>
    )
    
}

export default Youtube;
```

最終的な動き

![ezgif.com-video-to-gif.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379873/d5274a25-bf6c-86a6-84a1-fc1829074e7c.gif)


これでReactでYoutubeデータの取得から埋め込みまで出来ました。

以上です。