西武ライオンズ連覇を讃えつつReact + Typescriptに入門する


# はじめに
この資料は9/27の[Joolen](https://www.joolen.co.jp/)社内勉強会に使用する予定の資料です

---
# 告知！
１０月2日(水)に、松戸駅近くで[Laravel6.0のドキュメント(英文)をみんなで読もう！ #1](https://dev-com-matsudo.connpass.com/event/147362/)やります。よかったらきてください。
![](https://connpass-tokyo.s3.amazonaws.com/thumbs/36/69/3669e5d4c7be615962dd6c300fbb3bdd.png)

---
# まずは
埼玉西武ライオンズ、パ・リーグ連覇おめでとうございます:laughing:
![renpa.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154110/727b2a0b-f5a9-9f2d-9dad-8bc5e192ed5c.jpeg)

---

#ライオンズファンのエンジニアとして、この気持ちを技術で社内共有したい！<br>Reactの入門情報も合わせて伝えたら最高ではないか！

---

# と言うわけで、Reactについての簡単な説明
* facebook製のOSS。
* MVCで言う所のV(View)を司っている
* Javascriptで書かれている
* コンポーネント指向(小さなパーツの集まりで一つのページを構成する)

コンポーネント指向は個人的にはレゴブロックで理解しています。
ブロック = 小さなコンポーネント
完成物　= アプリケーションのページ

![](https://cdn3.movieweb.com/i/article/q80U0rCKHjnPsHpseT0y1cSkezrlUA/738:50/Rise-Of-Skywalker-Lego-Sets-Star-Wars-Toys.jpg)

---
# Typescriptとは

* マイクロソフトによって開発され、メンテナンスされているフリーでオープンソースのプログラミング言語
* Visual Studio Codeとの相性が半端じゃなく良い。
* 型定義ファイルをサポートしており、既存のJavaScriptライブラリに型情報を付与して利用できる。今までのJavascriptの変数にどんな型が入ってくるか分からない問題を根本から解決してくれる。
* 実際に動かすにはトランスパイルが必要。
* 既存のパッケージに型ファイルがない時には自分で作らなきゃいけない等、辛みもあるけれど、品質を保ちながら開発するには、かなり役立つ。

---

# 早速、作ってみる
`create-react-app`を使ってReactの空プロジェクトを生成しても良いですが、Joolenの[Reactボイラーテンプレート](https://github.com/joolen/react-project-template)があるのでこれを利用
`Node.js`と`git`はあらかじめインストールしておいてくださいね！

```sh
# gitからボイラーテンプレートをClone
git clone https://github.com/joolen/react-project-template.git renpa-lions
# ディレクトリを移動して
cd renpa-lions/
# 関連するパッケージをインストール(たくさんあるので、時間がかかる)
npm install
# npm start で起動
npm start
```

それだけで、ローカルへのReactの導入は完了です。この画面が`http://localhost:3000/`で出ればOK!
![スクリーンショット 2019-09-26 9.03.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154110/392a4128-40d9-07bc-3928-12d8b751065c.png)

---
## Lionsを讃えるための機能は以下の通りです。
入門編なので、あまり作り込みはしないように簡単な機能を作ります。

1. 公式twitterアカウントのタイムライン表示
![スクリーンショット 2019-09-26 9.43.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154110/573fa4a8-4410-4e1a-bad1-fb8e4cd3be8d.png)

2. 押すとランダムで各選手の顔写真と決め台詞が出てくるボタン
![sampleImage.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154110/320f09f0-5619-61f7-bf12-a77b03f6d328.gif)

---

## まずはタイムライン機能から
タイムライン機能を表示するためのコンポーネントが既に存在しているので、それをインストール。

```sh
npm install --save react-twitter-widgets
``` 

西武ライオンズの公式ツイッターアカウント名をパラメータにした表示用機能を作成

```javascript
import React from 'react';
import { Timeline as TL } from 'react-twitter-widgets'/*ここで必要なパッケージをインポート */

/**
 * Twitterのタイムラインを表示するコンポーネントをここで定義
 */
export const Timeline = () => {
    return (
        <TL dataSource={{
            sourceType: 'profile',
            screenName: 'lions_official'
        }}
            options={{
                username: 'lions_official',
                height: '600'
            }}
            onLoad={() => console.log('Timeline is loaded!')} />
    )
}
``` 
## これだけで、タイムライン機能は出来上がりです。

---
## コンポーネント指向の強さ
すでに開発済みのコンポーネントが世の中にはたくさんありますし(もちろん、選定は必要)、自作のコンポーネントを様々なページに流用することが可能です。
車輪の再発明を予防し、DRY(Don't Repeat Yourself)の法則を守りやすい作りができます。

---
# 次は特徴的な選手の決め台詞を表示します。

ソースコードはこんな感じになりました。

```javascript
import React, { useState, useCallback } from 'react';
import { Button } from '@material-ui/core';
import { makeStyles } from '@material-ui/core/styles';
import Card from '@material-ui/core/Card';
import CardActionArea from '@material-ui/core/CardActionArea';
import CardContent from '@material-ui/core/CardContent';
import CardMedia from '@material-ui/core/CardMedia';
import Typography from '@material-ui/core/Typography';

/**
 * ここで表示する情報の幅や高さを指定しています。
 */
const useStyles = makeStyles({
    card: {
        maxWidth: 345,
    },
    media: {
        height: 320,
    },
});
export const SignaturePhrase = () => {
    const classes = useStyles();
    /**
     * ここに初期値をセットします。ReactHooks定義
     */
    const [player, setPlayer] = useState({
        imageUrl: 'https://www.seibulions.jp/pc/_pl_img/old/company/img/img_2016petmark01.jpg',
        name: '決めゼリフ！',
        signiturePhrase: 'ここに出ます'
    });
    /**
     * ボタンが押された時の挙動
    */
    const setPhrase = useCallback(() => {//useCallbackを入れることで、無駄な関数の再生性を防ぎます。
        const target = getRandomInt(players.length);//誰が出るかはお楽しみ。
        setPlayer({
            imageUrl: players[target].imageUrl,
            name: players[target].name,
            signiturePhrase: players[target].sighniturePhrase
        })//React Hooks発動！ここで塗り替えられたStateは即座にページに反映されます。
    }, []);

    return (
        <div>
            <Button size='large' color='primary' variant='contained' onClick={setPhrase}>決め台詞を聞く！</Button>
            <Card className={classes.card}>
                <CardActionArea>
                    <CardMedia
                        className={classes.media}
                        image={player.imageUrl}
                        title="Logo"
                    />
                    <CardContent>
                        <Typography gutterBottom variant="h5" component="h2">
                            {player.name}
                        </Typography>
                        <Typography variant="h6" color="textSecondary" component="p">
                            {player.signiturePhrase}
                        </Typography>
                    </CardContent>
                </CardActionArea>
            </Card>
        </div>
    )
}
/**
 * データベース代わり。ここのデータを引っ張ってきています。
 */
const players = [
    { imageUrl: 'https://www.seibulions.jp/cmn/images/player/2019/ph_player_99.jpg', name: '#99 エルネスト・メヒア', sighniturePhrase: 'メヒアさまさまやー' },
    { imageUrl: 'https://www.seibulions.jp/cmn/images/player/2019/ph_player_5.jpg', name: '#5 外崎 修汰', sighniturePhrase: 'アップルパーンチ！' },
    { imageUrl: 'https://www.seibulions.jp/cmn/images/player/2019/ph_player_54.jpg', name: '#54 ザック・ニール', sighniturePhrase: 'あざーす！' },
    { imageUrl: 'https://www.seibulions.jp/cmn/images/player/2019/ph_player_33.jpg', name: '#33 山川　穂高', sighniturePhrase: 'どすこーい！' },
    { imageUrl: 'https://www.seibulions.jp/cmn/images/player/2019/ph_player_10.jpg', name: '#10 森　友哉', sighniturePhrase: '吉田打った？' },
]

function getRandomInt(max: number) {
    return Math.floor(Math.random() * Math.floor(max));
}
```

ここでは特に`material-ui`以外のライブラリは使わずに自分自身で書いています。

---

# React Hooksによりコンポーネント内の処理をかなり<br>シンプルに書けるようになったと思います。

---

# あとは表示したいページにコンポーネントを埋め込んであげるだけ

```javascript
import React from 'react';
import Typography from '@material-ui/core/Typography';
import Container from '@material-ui/core/Container';
import { Grid } from '@material-ui/core';
import { Timeline } from '../organisms/Timeline';
import { SignaturePhrase } from '../organisms/SignaturePhrase';

export const WelcomeTemplate = () => {
    return (
        <Container maxWidth="lg">
            <Grid container spacing={3}>
                <Grid item container justify='center'>
                    <Typography variant="h4" component="div" >
                        埼玉西武ライオンズ連覇おめでとうございます！
                    </Typography>
                    <img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154110/727b2a0b-f5a9-9f2d-9dad-8bc5e192ed5c.jpeg'
                        style={{ height: 300 }} />
                </Grid>
                <Grid item md={5}>
                    <Timeline /> {/* ここにTwitterのタイムライン機能を埋め込み */}
                </Grid>
                <Grid item md={7}>
                    <SignaturePhrase /> {/*ここは決め台詞を取得するところ */}
                </Grid>
            </Grid>
        </Container>
    )
}
```

---

# コンポーネントの開発を助けてくれるスペシャルなツール
Storybookというツールがあります。自分好みのコンポーネントのカタログを作ることができるので、非常に重宝しますよ！

![StoryBook.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154110/07911d47-ca1e-b915-50a5-867382a27b2a.gif)

---
# ありがとうございました！<br>クライマックスシリーズ、日本シリーズも楽しみます！
一緒に楽しんでくれる人も募集ー。
![](https://lions-static.s3.amazonaws.com/files/products/3641/3641.jpg)