🗂

【React】記事一覧からサムネイル画像をクリックで読み込んでみる

2023/02/20に公開

概要

ReactでJsonファイルからデータを読み込む処理はよくある。
とある案件で、データを読み込んだ一覧から、各一覧にボタンがあってクリックすると該当するデータを取ってくるという仕組みを作る事があったのでやってみる。

なので、今回はJsonファイルから記事の一覧をとってきてボタンをクリックするとサムネイル画像のデータを読み込む処理をやってみたいと思う。

今回のデモはこちら

仕様

今回はReactの他にいくつかライブラリを使用している。

  • react-image 画像を読み込む際にローディングの設定ができる
  • JsonはJsonplaceholderを利用

公式サイトは下記。
https://www.npmjs.com/package/react-image
https://jsonplaceholder.typicode.com/

Jsonファイルについて

Jsonはpostsデータとphotosデータを使う。
postsデータはid番号とtitle利用する。
photosデータはid番号とthumbnailUrlを利用します。

今回はpostsデータのid番号とphotosデータのid番号は同じ記事の扱いとします。

posts
[
{
"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として渡しておく。

App

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番号を利用する。

ThumbBlock
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>} />
     )}
    </>
  );
};

サムネイル画像を読み込む処理

「画像を読み込む」というボタンにクリックイベントgetThumbpropsで受けとったid番号を引数としてgetThumbに渡し、さらにreadThumbという関数を追加してる。

このreadThumbhttps://jsonplaceholder.typicode.com/photos/${id}というURLでid毎のURLでfetchでサムネイル画像のデータ(idthumbnailUrl)を取得してuseStateでセットする。
利用するデータがidthumbnailUrlなのでオブジェクトとして下記のようにセットしている。

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に関して。
インストールしたら、importImgというタグを利用できるようにする。
その後、Imgタグを使用して、src属性にサムネイル画像のURLを指定するのだが、ここで便利なのが、loader属性。
サンプルでは「読み込中」としてるが画像も指定できる。

import {Img} from 'react-image'
<Img css={imgThumb} src={thumbData.thumb_url} loader={<p>読み込身中</p>}

他にもuseImageというAPIや属性として、loaderのほかにunloadercrossoriginなどがあるので試すのもいい。

まとめ

今回、とりあえず自分で試してみて、正直もっときれいにコードまとめられると思うが、今回はこれで良しとする。
react-imageloading属性は便利だと思ったが、今度はSuspenceを利用して実験してみる。

Discussion