Help us understand the problem. What is going on with this article?

ReactでTwichのランキングサイトを作ってみよう!

More than 1 year has passed since last update.

はじめに

みなさんTwitchというゲームに特化した配信サイトはご存知でしょうか。Justin.tvからゲーム専用配信サイトとして派生し、現在はAmazon傘下にあるサイトです。今回はTwitchAPIとReactでランキングサイトを作成していこうと思います!

ゴール

以下のようなランキングサイトを作成していきたいと思います!!
※ 実際に動いているものがみたい方はこちらからご覧になれます。

スクリーンショット 2018-12-02 17.45.22.png

TwtichAPIキーの取得

こちらからAPIキーの取得を行なってください。
ログインを行い下記の画像の赤枠の部分で囲んである「アプリケーション登録」から登録を行なってください。
touroku.png

名前は使用されていると使えないため、日付など入れて良しなにしましょう。リダイレクト先はローカル環境を指定することが可能のためlocalalhost:3000にしました。下記の画像の赤枠で囲まれている部分がクライアントIDになります。以後使うため大切に保管してください。

スクリーンショット 2018-11-28 15.24.22.png

create-react-app

今回はcreate-react-appを使って雛形を作り、開発して行こうと思います。
create-react-appが使えない方はnpm install -g create-react-appでインストールしてください。

# create-react-app プロジェクト名
$ create-react-app twitch_ranking

create-react-appコマンドでアプリの雛形の作成が完了したら、作成したディレクトリに移動し以下のコマンドを実行してください。(--saveオプションはpackage.jsonのdependenciesに追加するために行なっています。)prop-typesはpropsの型チェックする際に使います。@material-ui/coreはReactでマテリアルデザインを扱うためのUIコンポーネントです。今回はデザイン全般を@material-ui/coreを使って実装します。@material-ui/iconsアイコンを扱うUIコンポーネントです。使い勝手の良いアイコンがたくさんあるため今回もユーザーアイコンなど様々なアイコンを使います。

# prop-typesコンポーネントのインストール
$ npm install --save prop-types
# @material-ui/coreコンポーネントのインストール
$ npm install --save @material-ui/core
# @material-ui/iconsコンポーネントのインストール
$ npm install --save @material-ui/icons

Headerの作成

サイトのヘッダーを作成していきます。プロジェクト名/src配下にHeader.jsファイルを作成してください。サイトのヘッダーはシンプルにタイトルのみの表示となります。ヘッダーのデザインは公式のAppBarを参考にしています。

src/Header.js
import React from 'react';
import PropTypes from 'prop-types';
import {withStyles} from '@material-ui/core/styles';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import Typography from '@material-ui/core/Typography';

const styles = {
  root: {
    width: '100%',
    backgroundColor: '#6441a5',
  },
};

const Header = (props) => {
  const {classes} = props;
  return (
    <AppBar position="static" className={classes.root}>
      <Toolbar>
        <Typography variant="title" style={{color: '#E6E6E6'}}>Twitchランキング(JP</Typography>
      </Toolbar>
    </AppBar>
  );
};

Header.propTypes = {
  // classesがobjectであるかチェック 
  classes: PropTypes.object.isRequired,
};

export default withStyles(styles)(Header);

ランキングページの作成

次にランキングページの作成をします。プロジェクト名/src/App.jsを以下のようにしてください。ここではfetchを用いてTwichAPIにGETリクエストを行なっています。headerClient-ID部分に先ほど取得したクライアントIDを入力してください。jsonを受け取る際にあらかじめ取得したjsonを格納する空の配列であるrespをstateで用意します。Streamsコンポーネントでは取得したresp内に格納されている配列をmap関数を用いて処理しています。最後に先ほど作成したヘッダーと組み合わせてあげればページの完成です。

src/App.js
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Header from './Header'

import Paper from '@material-ui/core/Paper'
import Typography from '@material-ui/core/Typography'
import GameIcon from '@material-ui/icons/Gamepad'
import ViewerIcon from '@material-ui/icons/Person'
// 後ほど作成する
import './twitch.css'


class TwitchRankPage extends Component {
  constructor(props) {
    super(props);
    this.state = {
      loading: true,
      resp: [],
    };
    // 取得件数を50に設定する。
    this.limit = 50
  }

  componentDidMount() {
    fetch(`https://api.twitch.tv/kraken/streams/?language=ja&limit=${this.limit}`, {
      method: 'get',
      headers: new Headers({
        'Accept': 'application/vnd.twitchtv.v5+json',
        'Client-ID': '先ほど取得したクライアントID',
      }),
    })
      .then((response) => response.json())
      .then((resp) => {
        this.setState({
          resp: resp.streams,
          loading: false,
        })
      })
      .catch((error) => {
        console.log("error", error)
      })
  }

  render() {
    const { resp } = this.state;
    return (
      <div>
        <Header />
        <Streams resp={resp} />
      </div>
    )
  }
}


const Streams = (props) => {
  const json = props.resp;
  let i = 1;

  return (
    <Paper className="paper">
      <table>
        <thead className="table-head">
          <tr>
            <th className="rank"><Typography variant={"subheading"}>順位</Typography></th>
            <th className="thumb"><Typography variant={"subheading"}>チャンネル</Typography></th>
            <th className="movie-title" />
            <th><Typography variant={"subheading"}>視聴者数</Typography></th>
          </tr>
        </thead>
        <tbody>
          {json.map((stream) => {
            return (
              <tr key={stream._id}>
                <td className="rank">
                  <Typography variant={"title"}>{i++}</Typography>
                </td>
                <td className="thumb">
                  <a href={stream.channel.url}>
                    <img src={stream.preview.medium} alt={stream.channel.status} />
                  </a>
                </td>
                <td className="detail">
                  <Typography variant={"title"} className="movie-title">{stream.channel.status}</Typography>
                  <Typography variant={"subheading"}>{stream.channel.display_name}</Typography>
                  <Typography variant={"subheading"} className="game-cat">
                    <GameIcon className="game-icon" />
                    <a href={"https://www.twitch.tv/directory/game/" + stream.channel.status}>
                      {stream.game}
                    </a>
                  </Typography>
                </td>
                <td className="viewers">
                  <Typography variant={"title"}>
                    <ViewerIcon className="viewer-icon" />
                    {stream.viewers}
                  </Typography>
                </td>
              </tr>
            )
          })}
        </tbody>
      </table>
    </Paper>
  )
};

Streams.propTypes = {
  rank: PropTypes.number,
  userName: PropTypes.string,
  movieTitle: PropTypes.string,
  gameTitle: PropTypes.string,
  viewers: PropTypes.number,
  url: PropTypes.string,
  thum: PropTypes.string,
};

export default TwitchRankPage

スタイルを整える

現在のままでは見栄えが悪いです。そのためプロジェクト名/src配下にtwitch.cssを作成し、以下のようにしましょう。

src/twitch.css
/* PC */
@media screen and (min-width: 767px) {
    root {
      padding: 0;
      margin: 0;
    }

    thead {
      padding-bottom: 100px;
    }

    .paper {
      text-align: center;
      margin: auto;
      margin-top: 30px;
      width: 1000px;
    }

    table {
      width: 1000px;
    }

    tr {
      border-bottom: 1px solid #c9c9c9;
      display: block;
      padding-top: 10px;
      padding-bottom: 10px;
    }

    .rank {
      width: 80px;
    }

    .thumb {
      padding: 0;
      width: 320px;
    }

    .detail {
      text-align: left;
      width: 380px;
      padding-left: 20px;
    }

    .movie-title {
      width: 380px;
      padding-right: 0;
      overflow: hidden;
      white-space: nowrap;
      text-overflow: ellipsis;
    }

    .game-cat {
      width: 380px;
      padding-left: 0;
      padding-right: 0;
      overflow: hidden;
      white-space: nowrap;
      text-overflow: ellipsis;
    }

    .viewers {
      padding-left: 20px;
      width: 150px;
    }

    .viewer-icon {
      padding-right: 10px;
      color: #ff0000;
      font-size: 30px;
      position: relative;
      top: 5px;
      left: 5px;
    }

    .game-icon {
      padding-right: 10px;
      color: #380B61;
      font-size: 25px;
      position: relative;
      top: 5px;
      left: 5px;
    }
}

/* SmartPhone */
@media screen and (max-width: 767px) {
  table {
    border-collapse: collapse;
    border-spacing: 0;
    width: 100vw;
    border: none;
  }

  thead {
    display: none;
  }

  tr {
    padding-top: 10px;
    padding-bottom: 10px;
    border-bottom: 1px solid #c9c9c9;
    display: block;
  }

  td {
    border-bottom: none;
    display: block;
    padding: 0;
  }

  .rank {
    display: none;
  }

  img {
      width: 100vw;
  }

  .detail {
    padding-right: 0;
  }

  .movie-title {
    width: 100vw;
    padding-right: 0;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
  }

  .game-cat {
    width: 380px;
    padding-left: 0;
    padding-right: 0;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
  }

  .viewers {
    padding-right: 30px;
    text-align: right;
  }

  .viewer-icon {
    padding-right: 10px;
    color: #ff0000;
    font-size: 30px;
    position: relative;
    top: 5px;
    left: 5px;
  }

  .game-icon {
    padding-right: 10px;
    color: #380B61;
    font-size: 25px;
    position: relative;
    top: 5px;
    left: 5px;
  }
}

完成

最後にindex.jsにTwitchRankPageコンポーネントを追加してください。コンソールからnpm startコマンドで実行して動いたら完成です。動かない方はエラーをよく読んでみてください。

src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import TwitchRankPage from './App';

ReactDOM.render(
  <TwitchRankPage />,
  document.getElementById('root')
);

まとめ

みなさんできましたでしょうか。cssなどを変更してかっこよくカスタマイズしてみてください。今回初めてTwitchAPIを使ってみましたが、充実していていろいろできそうに感じました。みなさんもTwichAPIを使って何か作ってみてはいかがでしょうか。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away