やること
ボタンをクリック → ローディング画面 → 結果
前提
以前作成した下記記事の環境からスタート
必要な材料
① ボタン
app.tsxのreturn内に<button>ボタン</button>
を追加する
② ローディング画面
以前、作成した下記記事を参考に作成します。
③ 結果画面
こちらも以前、作成した下記記事を参考に作成します。
ここまでで確認
必要な材料を全て統合した結果
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;
`;
- ターミナルで
yarn start
- 別のターミナルで
yarn mock
- ブラウザで
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は今回いらないので削除)
確認
- ターミナルで
yarn start
- 別のターミナルで
yarn mock
- ブラウザで
localhost:8111
にアクセス
画面上にボタンがあるので、クリックしLoadingが表示、5秒後にテスト テスト1 テスト2
されていたらOK!
最後に
簡易的ではありますが、ボタンをクリックしたら結果が出てくるまでの時間にLoadingを表示することができました。
今回はHooksのUseStateを多用してましたが、ここからReduxに置き換えていくとコードもスッキリするので良いと思います。
参考文献