犬無料10連ガチャを回していかないか？【React】


この記事は「[【マイスター・ギルド】本物のAdvent Calendar 2022]( https://adventar.org/calendars/7794)」5日目の記事です。
他の記事も要チェックや！！

# はじめに

みなさまこんにちは。sawaです。

「はよ犬ガチャを回させんかい！」という、犬ジャンキーの方は早速下のリンクからどうぞ。

https://dog-gacha.netlify.app/

犬ってかわいいですよね。
時間に余裕があってもなくても犬を見て癒されている方は多いんじゃないでしょうか。
今日はそんな犬の画像を提供してくれるAPIを紹介したいと思います。

ただ紹介するだけでは面白くないので、今回はReactで10連ガチャ風に実装してみました。
なぜReactなのかというと、勉強中だからです。
なぜ10連ガチャ風なのかというと、10連ガチャをたくさん回せて犬も見れるとなるととっても嬉しいからです。
対戦よろしくお願いします。


## 犬APIについて
犬好きの皆様にぜひ知っていただきたいAPIがあります。
その名は「DogAPI」。そのまんまですね。

https://dog.ceo/dog-api/about

これは[スタンフォード大学が提供している犬の画像のデータセット](http://vision.stanford.edu/aditya86/ImageNetDogs/)を、有志がAPIで取得できるようにしてくれたものです。

[犬種別に取得](https://dog.ceo/dog-api/breeds-list)できたり、エンドポイントに数字を指定して[複数取得](https://dog.ceo/dog-api/documentation/random)できたりします。
ブラウザ上で試せるので、自分で試したい方はリンク先へどうぞ。




また、自分ちの犬の写真をPRで投げると追加してくれるらしいです。
犬飼いエンジニアの方は参加してみてはいかがでしょうか。


https://github.com/jigsawpieces/dog-api-images#dog-api-images

# Reactで10連ガチャ風に実装する

早速作っていきましょう。


## 犬APIを叩く

まずは犬APIを叩きます。

```react:DogCard.jsx
import { useEffect, useState } from "react";
import axios from "axios";

export const DogCard = () => {
  const [posts, setPosts] = useState([]);

  //APIを叩いてpostsにJSONデータを格納
  useEffect(() => {
    axios.get("https://dog.ceo/api/breeds/image/random/10").then((res) => {
      setPosts(res.data.message);
    });
  }, [setPosts]);

  return (
    <SDogCard>
      <ul>
        {posts.map((post, i) => {
          return (
            <li key={i} >
              <img src={post} width={500} alt="犬の画像" loading="lazy"  />
            </li>
          );
        })}
      </ul>
    </SDogCard>
  );
};
```
### 結果
![localhost_3000_ (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2752085/64d6fd47-9a9e-bdd4-d01d-53d19306214f.png)

### 補足

- `https://dog.ceo/api/breeds/image/random/10`と、エンドポイントに「10」と指定することで10枚の画像を取得しました。10連ガチャだからね。
- 取得した画像の大きさがバラバラなので、一旦`<img src={post} width={500} alt="犬の画像" loading="lazy" />`としてwidthを指定しています。とりあえずの処置なので後で調整します。

## ボタンの実装

上述した実装方法だと画面更新で画像が切り替わるのですが、それでは味気ないので画像を表示するボタンを実装していきます。
「初回の結果を表示するボタン」と、「２回目以降の結果を表示するボタン」の２種類を作ります。

### 初回の結果を表示するボタンの実装

```react:Top.jsx
export const Top = () => {
  const [showDogImageFlag, setShowDogImageFlag] = useState(false);
  const onClickShowDogImage = () => {
    setShowDogImageFlag(!showDogImageFlag);
  };

  return (
    <STop>
      <h1 className="title">犬無料10連ガチャ</h1>
      <div
        className="showResultArea"
        showresult={showDogImageFlag ? "false" : "true"}
      >
        <DogCard />
      </div>
      <div className="showResultButtonArea">
        <input
          type="button"
          value="回す！"
          onClick={onClickShowDogImage}
          className="showResultButton"
          showresult={showDogImageFlag ? "false" : "true"}
        />
      </div>
    </STop>
  );
};
```
``` css
/*犬の画像を表示/非表示*/
  .showResultArea[showresult="false"] {
    display: inline-block;
    padding: 20px;
  }
  .showResultArea[showresult="true"] {
    display: none;
  }

/*ボタンを表示/非表示*/
  .showResultButton[showresult="true"] {
    display: inline-block;
  }
  .showResultButton[showresult="false"] {
    display: none;
  }
```

### 結果
![スクリーンショット_20221205_155829.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2752085/017a956d-f902-bac1-89be-ce5d608b8148.png)
サイトの初回表示時にこの画面が表示されます。
この赤矢印で示しているボタンを押すと結果が表示されます。

### 補足
- ボタンを押すと`showDogImageFlag`の真偽値が切り替わるようになっています。それを受けてCSSで犬画像とボタンの表示非表示を制御しています。
- タイトルやボタンのスタイルは適当にCSSを当てています。グラデーションを設定するだけで情報量が多くなるので適当にそれっぽいスタイルを当てたいときにオススメです。

### ２回目以降の結果を表示するボタンの実装
```diff_javascript:DogCard.jsx
export const DogCard = () => {
  const [posts, setPosts] = useState([]);
+  const [renew, setRenew] = useState("");

  //APIを叩いてpostsにJSONデータを格納
  useEffect(() => {
    axios.get("https://dog.ceo/api/breeds/image/random/10").then((res) => {
      setPosts(res.data.message);
    });
-  }, [setPosts]);
+  }, [setPosts, renew]);

  return (
    <SDogCard>
      <ul>
        {posts.map((post, i) => {
          return (
            <li key={i}>
              <img src={post} alt="犬の画像" loading="lazy" />
            </li>
          );
        })}
      </ul>
+      <input
+       type="button"
+       value="もう一回！"
+       onClick={() => setRenew((renew) => renew + 1)}
+      />
    </SDogCard>
  );
};
```
### 結果
![スクリーンショット_20221205_155938.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2752085/3262d672-4532-ead8-1077-c16f0d8c7bda.png)

赤矢印で指し示しているボタンを押すと、新しい結果画面が読み込まれます。

### 補足

- 「もう一回！」ボタンを押す度にAPIが叩かれるように、新たにstateを追加しました。
こちらは下記サイト様を参考にさせていただきました。ありがとうございます！

https://mseeeen.msen.jp/react-implement-button-to-refresh-data-on-user-action/


## CSSで結果画面をガチャっぽくする

ガチャの結果画面で気になるものといえば、そう、レアリティですね。

figmaで適当に画像をつくります。
![SSR.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2752085/3c1e7a64-c790-6ead-6d22-0b3e3584f10b.png)

それを画像の左上に重ねて、画像の大きさを均一にしたりします。
![スクリーンショット_20221205_165047.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2752085/afadc2a6-4ab0-d830-f625-08be7eae873b.png)

おお！一気にガチャっぽくなりましたね。

ちなみに**この世の犬はみな等しく可愛いので全員SSRです。**

## ローディングを入れる

```diff_javascript
export const DogCard = () => {
  const [posts, setPosts] = useState([]);
  const [renew, setRenew] = useState("");
+  const [isLoading, setIsLoading] = useState(true);

  //APIを叩いてpostsにJSONデータを格納
  useEffect(() => {
+    setIsLoading(true);
    axios
      .get("https://dog.ceo/api/breeds/image/random/10")
      .then((res) => {
        setPosts(res.data.message);
+        setIsLoading(false);
      })
      .catch((error) => {
        console.log(error);
+        setIsLoading(true);
      });
  }, [setPosts, renew]);

  return (
    <SDogCard>
      <div>
+        {isLoading ? (
+          <div className="spinner-box">
+            <div className="three-quarter-spinner"></div>
+          </div>
+        ) : (
          <ul className="dogsList">
            {posts.map((post, i) => {
              return (
                <li key={i} className="dogCard">
                  <img src={post} alt="犬の画像" className="dogImg" loading="lazy" />
                  <img src={ssrImg} alt="SSRラベル" className="ssrImg" />
                </li>
+              );
            })}
          </ul>
+        )}
        <input
          type="button"
          value="もう一回！"
          onClick={() => setRenew((renew) => renew + 1)}
          className="showRenewButton"
        />
      </div>
    </SDogCard>
  );
};
```
ローディングのぐるぐるが欲しかったので追加しました。

ローディングは下記サイト様を参考にさせていただきました。ありがとうございます！

https://yoheiko.com/blog/%E3%81%90%E3%82%8B%E3%81%90%E3%82%8B%E5%9B%9E%E3%82%8B%E3%83%AD%E3%83%BC%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0%E7%94%BB%E9%9D%A2%E3%81%AE%E5%AE%9F%E8%A3%85%E6%96%B9%E6%B3%95-react%E3%81%AA%E3%82%89/
# 完成

これで完成です。
あとは好きなだけガチャを回してください！

https://dog-gacha.netlify.app/


ちなみにレスポンシブ対応しているので、スマホでも回せます！やったね！

![スクリーンショット_20221205_165356.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2752085/1676734c-7a2b-3ea7-7104-64341a6a4083.png)


# おわりに

ここまで読んでいただきありがとうございました！
ボーナスとしてうちの犬の写真を置いておきます。

![DSC_3940.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2752085/6d411d8e-4c1d-048c-6f9b-90b18266273f.jpeg)


かわい～～～～～～ッッッッッ！！！！