【React】記事一覧からサムネイル画像をクリックで読み込んでみる
概要
ReactでJsonファイルからデータを読み込む処理はよくある。
とある案件で、データを読み込んだ一覧から、各一覧にボタンがあってクリックすると該当するデータを取ってくるという仕組みを作る事があったのでやってみる。
なので、今回はJsonファイルから記事の一覧をとってきてボタンをクリックするとサムネイル画像のデータを読み込む処理をやってみたいと思う。
今回のデモはこちら
仕様
今回はReactの他にいくつかライブラリを使用している。
- react-image 画像を読み込む際にローディングの設定ができる
- JsonはJsonplaceholderを利用
公式サイトは下記。
Jsonファイルについて
Jsonはpostsデータとphotosデータを使う。
postsデータはid番号とtitle
利用する。
photosデータはid番号とthumbnailUrl
を利用します。
今回はpostsデータのid番号とphotosデータのid番号は同じ記事の扱いとします。
[
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
},
{
"userId": 1,
"id": 2,
"title": "qui est esse",
"body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"
}
]
photosデータは下記です。サムネイル画像のURLとid番号だけ使用します。
例えば、https://jsonplaceholder.typicode.com/photos/1
というid番号が1だと以下のデータです。
{
"albumId": 1,
"id": 1,
"title": "accusamus beatae ad facilis cum similique qui sunt",
"url": "https://via.placeholder.com/600/92c952",
"thumbnailUrl": "https://via.placeholder.com/150/92c952"
}
実装
実際に実装の流れとしては以下です。ReactのuseState
とかの説明は省く。
jsonデータを読み込む
まずはpostsデータのjsonデータを読み込む処理。
useEffect
でpostsデータを読み込んでmap()
で一覧表示する。
その際に、後術するThumbBlock
というコンポーネントにJsonデータから記事のidをprops
として渡しておく。
import React, { useEffect, useState } from "react";
import ThumbBlock from "../components/ThumbBlock";
const App = () => {
const [data, setData] = useState([]);
useEffect(() => {
const readData = async () => {
let res = await fetch("https://jsonplaceholder.typicode.com/posts");
let data = await res.json();
console.log(data);
setData(data);
};
readData();
}, []);
return (
<div css={wrapper}>
{data.map((d, idx) => {
return (
<div css={boxWrapper} key={idx}>
<p css={ttl}>{d.title}</p>
<ThumbBlock id={d.id} />
</div>
);
})}
</div>
);
};
サムネイル画像を読み込むコンポーネント
サムネイル画像用のコンポーネントThumbBlock
を作成して受けとったid番号を利用する。
import {Img} from 'react-image'
const ThumbBlock = ({ id }) => {
const thumb_id = id;
const [thumbData, setThumbData] = useState({});
const getThumb = (id) => {
const readThumb = async (id) => {
let res = await fetch(
`https://jsonplaceholder.typicode.com/photos/${id}`
);
let get_thumb = await res.json();
setThumbData({ id: get_thumb.id, thumb_url: get_thumb.thumbnailUrl });
};
if (thumbData.length === 0) {
try {
readThumb(id);
} catch (error) {
console.log(error);
}
} else {
if (id === thumbData.id) {
return;
} else {
readThumb(id);
}
}
};
return (
<>
<p css={btnThumb} onClick={() => getThumb(thumb_id)}>画像を読み込む</p>
{thumbData.thumb_url && (
<Img css={imgThumb} src={thumbData.thumb_url} loader={<p>読み込身中</p>} />
)}
</>
);
};
サムネイル画像を読み込む処理
「画像を読み込む」というボタンにクリックイベントgetThumb
にprops
で受けとったid番号を引数としてgetThumb
に渡し、さらにreadThumb
という関数を追加してる。
このreadThumb
でhttps://jsonplaceholder.typicode.com/photos/${id}
というURLでid毎のURLでfetch
でサムネイル画像のデータ(id
とthumbnailUrl
)を取得してuseState
でセットする。
利用するデータがid
とthumbnailUrl
なのでオブジェクトとして下記のようにセットしている。
setThumbData({ id: get_thumb.id, thumb_url: get_thumb.thumbnailUrl });
サムネイル画像を読むための条件
読み込む処理は、Reactでは定番といえる処理だが、問題もある。
既に読み込まれた画像・同じボタンをクリックされても困るので以下の条件が必要。
-
thumbData
にサムネイル画像データがある場合は処理を実行しない、ある場合は処理を実行。 -
thumbData
にサムネイル画像データがある場合にサムネイル画像のidとクリックしたidが同じ場合は同じボタンをクリックしたと判定して処理を実行しない。
そのための条件分岐が以下である。
if (thumbData.length === 0) {
try {
readThumb(id);
} catch (error) {
console.log(error);
}
} else {
if (id === thumbData.id) {
return;
} else {
readThumb(id);
}
}
これで同じボタンをクリックしても大丈夫だった。
react-imageについて
まずはreact-image
に関して。
インストールしたら、import
でImg
というタグを利用できるようにする。
その後、Img
タグを使用して、src
属性にサムネイル画像のURLを指定するのだが、ここで便利なのが、loader
属性。
サンプルでは「読み込中」としてるが画像も指定できる。
import {Img} from 'react-image'
<Img css={imgThumb} src={thumbData.thumb_url} loader={<p>読み込身中</p>}
他にもuseImage
というAPIや属性として、loader
のほかにunloader
やcrossorigin
などがあるので試すのもいい。
まとめ
今回、とりあえず自分で試してみて、正直もっときれいにコードまとめられると思うが、今回はこれで良しとする。
react-image
のloading
属性は便利だと思ったが、今度はSuspence
を利用して実験してみる。
Discussion