Reactで条件分岐

## やること
ボタンをクリック → ローディング画面 → 結果

## 前提
以前作成した下記記事の環境からスタート

https://qiita.com/sh19982580/items/5f71856c7949c494d769

## 必要な材料
①　ボタン
    app.tsxのreturn内に`<button>ボタン</button>`を追加する

② ローディング画面
以前、作成した下記記事を参考に作成します。

https://qiita.com/sh19982580/items/70a6fcdb29826513bea2

③ 結果画面
こちらも以前、作成した下記記事を参考に作成します。

https://qiita.com/sh19982580/items/eaa599f8b8c74bb4d87e

## ここまでで確認
必要な材料を全て統合した結果
app.tsx

    import React, { FC, useEffect, useState } from "react";
    import { Reset } from "styled-reset";
    import styled, { keyframes } from "styled-components";

    import { Content } from "./component/content";
    import { Ranking } from "./types/type";

    const App: FC = () => {
    const [ranking, setRanking] = useState<Ranking[]>([]);

    useEffect(() => {
        const fetchData = async () => {
        const response = await fetch("http://localhost:3000/ranking");
        const rankingData = await response.json();
        setRanking(rankingData);
        };
        fetchData();
    }, []);
    return (
        <>
        <Reset />
        <button>ボタン</button>
        <Load>Loading</Load>
        <Content ranking={ranking} />
        </>
    );
    };
    export default App;

    const opacityLoad = keyframes`
    0% {
        left: 0;
        opacity: 0;
    }
    10% {
        left: 10%;
        opacity: 0.5;
    }
    30% {
        left: 30%;
        opacity: 1;
    }
    50% {
        left: 50%;
        opacity: 1;
    }
    70% {
        left: 70%;
        opacity: 0;
    }
    100% {
        left: 100%;
        opacity: 0;
    }
    `;
    const Load = styled.p`
    animation: ${opacityLoad} 2.3s linear infinite;
    font-size: 50px;
    position: relative;
    width: 300px;
    `;

content.tsx

    import React, { FC } from "react";
    import styled from "styled-components";

    import { RankingData } from "../types/type";

    export const Content: FC<RankingData> = (props) => {
    const { ranking } = props;
    return (
        <>
        <StyledH1>Hello Hello</StyledH1>
        <table>
            <tr>
            {ranking.map(({ word }) => {
                return (
                <tr>
                    <td key={word}>{word}</td>
                </tr>
                );
            })}
            </tr>
        </table>
        </>
    );
    };

    const StyledH1 = styled.h1`
    font-size: 50px;
    text-align: center;
    `;

1. ターミナルで`yarn start`
1. 別のターミナルで`yarn mock`
1. ブラウザで`localhost:8111`にアクセス
画面上に`ボタン`,`テスト　テスト1 テスト2`,`Hello Hello`,`Loading`が表示されていたらOK！

## 条件分岐
今のままでは、全てが表示されている状態なので、ここから階層とコードを分けていきます。

    app.tsx --- main.tsx(条件分岐) --- loading.tsx
                                 |
                                 --- ranking.tsx(content.tsxからファイル名変更)


app.tsx

    import React, { FC, useEffect, useState } from "react";
    import { Reset } from "styled-reset";

    import { Main } from "./component/main";
    import { Ranking } from "./types/type";

    const App: FC = () => {
    const [ranking, setRanking] = useState<Ranking[]>([]);
    const [loading, setLoading] = useState<string>("");

    useEffect(() => {
        const fetchData = async () => {
        const response = await fetch("http://localhost:3000/ranking");
        const rankingData = await response.json();
        setRanking(rankingData);
        };
        fetchData();
    }, []);
    const search = () => {
        setLoading("Loading");
        // 変更を見るため、意図的に遅らせている
        setTimeout(() => setLoading("Ranking"), 5000);
    };

    return (
        <>
        <Reset />
        <button onClick={() => search()}>ボタン</button>
        <Main loadingData={loading} ranking={ranking} />
        </>
    );
    };
    export default App;

main.tsx

    import React, { FC } from "react";
    import { Data } from "../types/type";
    import { Loading } from "./loading";
    import { Ranking } from "./ranking";

    export const Main: FC<Data> = (props) => {
    const { ranking, loadingData } = props;
    return (
        <main>
        {loadingData === "Loading" && <Loading />}
        {loadingData === "Ranking" && <Ranking ranking={ranking} />}
        </main>
    );
    };

Dataの型である`types>type.d.ts`に下記を追加

    export interface Data {
      ranking: Ranking[];
      loadingData: string;
    }

loading.tsx

    import React, { FC } from "react";
    import styled, { keyframes } from "styled-components";

    export const Loading: FC = () => {
    return <Load>Loading</Load>;
    };

    const opacityLoad = keyframes`
    0% {
        left: 0;
        opacity: 0;
    }
    10% {
        left: 10%;
        opacity: 0.5;
    }
    30% {
        left: 30%;
        opacity: 1;
    }
    50% {
        left: 50%;
        opacity: 1;
    }
    70% {
        left: 70%;
        opacity: 0;
    }
    100% {
        left: 100%;
        opacity: 0;
    }
    `;
    const Load = styled.p`
    animation: ${opacityLoad} 2.3s linear infinite;
    font-size: 50px;
    position: relative;
    width: 300px;
    `;

※app.tsxにいたものを分解させただけ

ranking.tsx

    import React, { FC } from "react";

    import { RankingData } from "../types/type";

    export const Ranking: FC<RankingData> = (props) => {
      const { ranking } = props;
      return (
        <>
          <table>
            <tr>
              {ranking.map(({ word }) => {
                return (
                  <tr>
                    <td key={word}>{word}</td>
                  </tr>
                );
              })}
            </tr>
          </table>
        </>
      );
    };

※こちらもcontent.tsxにいたものをファイル名を変更しただけ(Hello Helloは今回いらないので削除)

#### 確認
1. ターミナルで`yarn start`
1. 別のターミナルで`yarn mock`
1. ブラウザで`localhost:8111`にアクセス
画面上にボタンがあるので、クリックしLoadingが表示、5秒後に`テスト　テスト1 テスト2`されていたらOK！

## 最後に
簡易的ではありますが、ボタンをクリックしたら結果が出てくるまでの時間にLoadingを表示することができました。
今回はHooksのUseStateを多用してましたが、ここからReduxに置き換えていくとコードもスッキリするので良いと思います。

## 参考文献

https://qiita.com/SLEAZOIDS/items/4f3c1b77d291eef548af

https://qiita.com/suin/items/99aa8641d06b5f819656