失恋駆動開発: ReactでサクッとTinder風モックを作る

こちらの記事は<a href="https://qiita.com/advent-calendar/2019/nijibox">NIJIBOX Advent Calendar 2019</a>の23日目の記事です。（遅刻してごめんなさい・・・）

# 前置き
こんにちは、ニジボックスのフロントエンドチームのこじこじです。
あっという間にクリスマスですね。街はキラキラ、大人も子どもも恋人たちも浮かれてしまう楽しい季節です。

## 突然ですが、失恋しました
そんな楽しい季節がやってきたにも関わらず、少し前に失恋しました。
クリスマスまでもう時間がない・・・！
ということで、クリスマスに自尊心を高めて過ごせるTinder風モックをサクッと作ってみることにしました。
落ち込んでいる暇はないのです。いのち短し、作れよ乙女。

## 作りたいもの
- なんか私めっちゃいいねきてる・・・モテ期なのでは・・・？！と思うためだけのTinder風webアプリ
- クリスマスに暇を潰せるレベルでいいので今回はモックアップの作成

## 書くこと
- Reactで簡単にTinder風UIを実装する方法
- サクッとモックを作りたい時に便利なツールの紹介

## 仕様技術・ツールなど
- <a href="https://codesandbox.io/" target="_blank">CodeSandbox</a>
- <a href="https://ja.reactjs.org/" target="_blank">React</a> - v15.6.2 ※バージョンが古い理由は後述します・・
- <a href="https://www.npmjs.com/package/react-swipe-card/v/0.1.4" target="_blank">React-swipe-card</a> - v0.1.4
- <a href="https://randomuser.me/" target="_blank">Random User Generator</a>
- 失恋に負けない気持ち(お好みで)

# 実装

## 1. サクッと開発環境構築： CodeSandbox
今回のテーマは「サクッと」モックを作成することなので、開発環境はオンラインIDEの<a href="https://codesandbox.io/" target="_blank">CodeSandbox</a>を利用しました。
無料かつ会員登録も不要で、2クリックで開発環境が整ってしまいます。

### 1-1. CodeSandboxにアクセスし、「Create a Sandbox, it's free」をクリック

![codesandbox.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269370/8f569705-3de0-2b05-4db6-6135bc3ff1fe.png)
会員登録はgithub連携でサクッと行うこともできますが、CodeSandbox上でコードを書くだけならば会員登録をしなくてもOKです。

### 1-2. テンプレートを選択
![codesandbox2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269370/3c7f3bb5-2631-e944-6882-cad92d42ef09.png)
実装に用いるテンプレートを選択します。今回はReactを選択。

![codesandbox2_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269370/cb4b6439-b558-d543-46a7-3568bb7db2da.png)
これで環境構築は完了。あとはコードを書くのみです。


## 2. サクッとTinder風UI実装: react-swipe-card
さて、いよいよTinder風UIの実装です。
左右のスワイプを判定・処理するコードをスクラッチで書くのはかなりしんどい・・・
でも心配ご無用。今回はReactでTinder風のスワイプをサクッと実装できちゃう<a href="https://www.npmjs.com/package/react-swipe-card/v/0.1.4" target="_blank">React-swipe-card</a>を使います。

### ここで注意事項
このReact-swipe-card、残念ながらlast updateが3年前で、React v16へのアップデートに対応しておらず、v16以上だとエラーでこけてしまいます・・・。
今回はクリスマスまでにサクッとつくることを優先でv15で実装を進めました。
（余談ですがこんな場合もCodeSandboxはパッケージのバージョンをプルダウンですぐ切り替えできちゃうからとってもベンリ）

### 2-1. React-swipe-cardをインストール
CodeSandboxなら、npmパッケージのインストールも「Add Dependency」ボタンをクリックして
![codesandbox3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269370/9ab3360f-d806-6668-6486-8435023c499d.png)
インストールしたいパッケージを検索・選択するだけでOK。お手軽すぎる〜。
![codesandbox5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269370/9c1eee10-37bd-3c99-8965-245e3ee83624.png)

### 2-2. カードのコンポーネントを作成
React-swipe-cardではCardsとCardの2つのコンポーネントが用意されています。
それぞれpropsで以下の設定を渡すことができます。

#### Cardsコンポーネント
- onEnd: 全てのカードをスワイプした時に実行する関数
- alertLeft: 左にスワイプした際に表示するコンポーネント
- alertRight: 右にスワイプした際に表示するコンポーネント
- alertTop: 上にスワイプした際に表示するコンポーネント
- alertBottom: 下にスワイプした際に表示するコンポーネント

#### Cardコンポーネント
- onSwipeLeft: 左にスワイプした時に実行する関数
- onSwipeRight: 右にスワイプした時に実行する関数
- onSwipeTop: 上にスワイプした時に実行する関数
- onSwipeBottom: 下にスワイプした時に実行する関数

今回はCardBoardという名前でカードのコンポーネントを作成することにします。
カードを右にスワイプしたら「マッチしました！」のアラートが表示され、
全てスワイプしきると「いいねはありません」のアラートが表示されます。

``` jsx:CardBoard.js
import React from "react";
import Cards, { Card } from "react-swipe-card";

// 左にスワイプした時に表示する要素
const CustomAlertLeft = () => <span>ごめんあそばせ</span>;

// 右にスワイプした時に表示する要素
const CustomAlertRight = () => <span>よろこんで</span>;

// 全てのカードをスワイプした後の処理
const showEndMessage = () => {
  alert("いいねはありません");
};

// 左にスワイプした時の処理
const handleSwipeLeft = () => {
  return;
};

// 右にスワイプした時の処理
const handleSwipeRight = () => {
  alert("マッチしました！");
};

// 表示するユーザー情報
const data = ["反町●史", "向●理", "大沢た●お"]

const CardBoard = () => {
  return (
    <Cards
      alertRight={<CustomAlertRight />}
      alertLeft={<CustomAlertLeft />}
      onEnd={showEndMessage}
      className="master-root"
    >
      {data.map((item, i) => {
        return (
          <Card
            key={i}
            onSwipeLeft={handleSwipeLeft}
            onSwipeRight={handleSwipeRight}
          >
            <h2>{item}</h2>
          </Card>
        );
      })}
    </Cards>
  );
};

export default CardBoard;

```

以下のようなモックが出来上がりました。
![demo.mov.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269370/f3c7ded5-e05a-93af-f754-46469f1938eb.gif)

左にスワイプしていますが反町●史も好きです。

デモは<a href="https://csb-jtkxb-5j0kmxn87.now.sh/" target="_blank">こちら</a>

## 3. サクッとダミーユーザー生成: Random User Generator
それっぽさを出すために、ユーザーの画像と名前を表示しましょう。

大量のダミーのユーザー情報が欲しい・・・
そんな時に使いたいのが<a href="https://randomuser.me/" target="_blank">Random User Generator</a>。

なんと無料でランダムなユーザー情報を返してくれるAPIです。
しかもその情報量は名前、サムネイル画像（サイズ違い3種）、住所からログインID・パスワードまで豊富・・・！

今回はパラメーターとして`gender=male`と`results=114`を渡して、男性の情報のみ114件を返すようにリクエストしました。
表示件数が114件なのはキリのいい数字だとなんとなくそれっぽさが出ないという理由だけです。
モテ期を感じることを最優先にしているのでパフォーマンスは考慮していません。
これを先ほど作ったCardBoardコンポーネントに渡してあげればOK。

```jsx:App.js
import React from "react";
import axios from "axios";
import CardBoard from "./CardBoard";
import handleResponse from "../utility/handleResponse";

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      userList: [],
      userCount: ""
    };

    this.getUserInfo = this.getUserInfo.bind(this);
  }

  getUserInfo() {
    return axios
      .get("https://randomuser.me/api/", {
        params: { results: 114, gender: "male" }
      })
      .then(result => handleResponse(result))
      .then(result => {
        const userList = [];
        for (let i = 0; i < result.length; i++) {
          userList.push({
            name: result[i].name.last,
            img: result[i].picture.large,
            age: result[i].dob.age
          });
        }
        const userCount = userList.length;
        this.setState({ userList, userCount });
      });
  }

  componentDidMount() {
    this.getUserInfo();
  }

  render() {
    return (
      <div className="App">
        <h1>
          あなたに{this.state.userCount}人から
          <br />
          いいねが届いています
        </h1>
        <CardBoard userList={this.state.userList} />
      </div>
    );
  }
}

export default App;

```

CardBoardコンポーネントの方も、propsを受け取ってカードを表示するように書き換えます。

```jsx:CardBoard.js
import React from "react";
import Cards, { Card } from "react-swipe-card";

// 左にスワイプした時に表示する要素
const CustomAlertLeft = () => <span>ごめんあそばせ</span>;

// 右にスワイプした時に表示する要素
const CustomAlertRight = () => <span>よろこんで</span>;

// 全てのカードをスワイプした後の処理
const showEndMessage = () => {
  alert("いいねはありません");
};

// 左にスワイプした時の処理
const handleSwipeLeft = () => {
  return;
};

// 右にスワイプした時の処理
const handleSwipeRight = () => {
  alert("マッチしました！");
};

- // 表示するユーザー情報
- const data = ["反町●史", "向●理", "大沢た●お"]
-
- const CardBoard = () => {
+ const CardBoard = props => {
  return (
    <Cards
      alertRight={<CustomAlertRight />}
      alertLeft={<CustomAlertLeft />}
      onEnd={showEndMessage}
      className="cards"
    >
-       {data.map((item, i) => {
+       {props.userList.map((item, i) => {
        return (
          <Card
            key={i}
            onSwipeLeft={handleSwipeLeft}
            onSwipeRight={handleSwipeRight}
          >
-           <h2>{item}</h2>
+           <div className="card__img">
+             <img src={item.img} alt="" />
+           </div>
+           <div className="card__info">
+             <div className="card__name">{item.name}</div>
+             <div className="card__age">{item.age}歳</div>
+           </div>
          </Card>
        );
      })}
    </Cards>
  );
};

export default CardBoard;

```

あとはスタイルを整えて、こんな感じになりました。
![sample2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269370/9976bf06-fddd-19e5-f205-d43955f0a379.gif)
国際色豊かにモテ期が来たようです。


コードは<a href="https://codesandbox.io/embed/mytinder-evjkz?fontsize=14&hidenavigation=1&theme=dark" target="_blank">こちら</a>

# まとめ
- サクッとモックアップやプロトタイプを作成したい場合はCodeSandboxが便利
- サクッとダミーユーザー情報が欲しい場合はRandom User Generatorがおすすめ
- サクッとTinder風UIをReactで実装したいならReact-swipe-cardが簡単だが、React v16以上で使えないため要検討（次の機会に調査します・・・）
- サクッと自尊心が高まるかどうかは自分次第