画面を読み込むときにAPIを呼び出してデータが取得されるまでローディングマーク(丸がぐるぐるするやつ)を表示したい、という場面は多いでしょう。
今回はReact + TypeScript + react-loadingを使って実装します。
また、APIを実装するのは手間なので以下のAPIを叩いてデータを取得します。
https://api.thecatapi.com/api/images/get?format=json&results_per_page=10&type=gif
(可愛い猫ちゃんの画像が取得されます。)
react-loadingとは?
ローディングアイコンを実装するためのサードパーティ製ライブラリです。
最新のコミットが2年前なので開発はほぼ止まっているようです。
以下サンプルコードのようにpropsで色やアイコンの大きさを調整することができます。
import React from 'react';
import ReactLoading from 'react-loading';
const Example = ({ type, color }) => (
<ReactLoading type={type} color={color} height={667} width={375} />
);
export default Example;
環境
- TypeScript 4.8.4
- React 18.0.21
- react-loading 2.0.3
- Ubuntu20.04
事前準備
まずはreactのプロジェクトを作成します。
$ npx create-react-app --template typescript loading
続いて、react-loadingをプロジェクトにインストールします。
$ cd loading
$ npm install react-loading
正常にインストールできれば準備完了です。
ディレクトリ構成
ディレクトリ構成は以下の通りです。
loading
├── src
├── components
├── image.ts # APIのレスポンスの型を定義するファイル
└── Images.tsx # 画面を描画するコンポーネント
└── useFetchImages.tsx # APIから画像を取得するカスタムフック
実装例
APIから取得するデータの型定義
https://api.thecatapi.com/api/images/get?format=json&results_per_page=10&type=jpg
で取得できるjsonデータは「id」「url」「source_url」という3つのプロパティを持ちます。(型はいずれもstring)
レスポンスの型をImageとして定義しましょう。
export type Image = {
id: string;
url: string;
source_url: string;
};
画面を描画する
import { useEffect} from 'react';
# react-loadingをインポートする
import ReactLoading from "react-loading";
# Image型をインポート
import { Image } from './image';
import { useGetImages } from './useGetUsers';
export const Images = () => {
const { isLoading, getImages, images } = useGetImages();
useEffect(() => getImages(), []);
return (
<>
{isLoading ?
<ReactLoading
type="spin"
color="black"
height="20px"
width="20px"
className="mx-auto"
/> :
images.map((image: Image) => (
<div key={ image.id}>
<img src={image.url} style={ {width:"150px", height:"150px"}} />
</div>
))
}
</>
);
};
APIから画像を取得するカスタムフック
import axios from 'axios';
import { useCallback, useState } from 'react';
import { Image } from './image';
export const useGetImages = () => {
const [isLoading, setIsLoading] = useState(false);
const [images, setImages] = useState<Image[]>([]);
const getImages = useCallback(() => {
setIsLoading(true);
axios.get<Image[]>('https://api.thecatapi.com/api/images/get?format=json&results_per_page=10&type=jpg')
.then((res) => {
setTimeout(() => {
setImages(res.data);
setIsLoading(false)
}, 3000);
})
.catch(() => {
alert('にゃーん');
setIsLoading(false);
})
},[images]);
return { isLoading, getImages, images };
};
重要なポイントは以下の3点です。
- ローディング状態をboolean型のState isLoadingとして定義する。(ローディング中:true, ローディング中ではない:false )
- API呼び出しを開始したらisLoadingをtrueに切り替える。成否にかかわらずAPI呼び出しが完了したらisLoadingをfalseに変更する。
- Image.tsxではisLoadingがtrueであるときにローディングアイコンを表示する。isLoadingがfalseであるときには取得したデータを表示する。
また、API呼び出しやisLoading Stateの状態更新のようなUI描画と直接関係のない処理はカスタムフックに切り出すとImage.tsxの見通しがよくなります。
参考資料
Reactに入門した人のためのもっとReactが楽しくなるステップアップコース完全版
react-loading -npm
独自フックの作成