👌

【Next.js】読み込み時にAPIからデータを取ってくる

2022/09/13に公開

概要

Next.jsでクリックしたときに外部APIからデータを取ってきて表示するとかある。
最初にそのページにアクセスした時に外部APIからデータを表示させたい場合getServerSidePropsで処理するメモ。

外部API

今回は犬の画像を検索できるサービスのAPIを利用してみる
https://dog.ceo/dog-api/

APIのURLは下記になる。

https://dog.ceo/api/breeds/image/random

取得できるJSONファイルのデータは下記

{
  "message": "https://images.dog.ceo/breeds/terrier-patterdale/patterdale-terrier-1330018870tnN.jpg",
  "status": "success"
}

外部APIを利用する際の注意点

外部APIから画像を取得する場合に以下のようなエラーが出たりする。

Error: Invalid src prop (https://images.dog.ceo/breeds/chihuahua/n02085620_7700.jpg) on `next/image`, hostname "images.dog.ceo" is not configured under images in your `next.config.js`
See more info: https://nextjs.org/docs/messages/next-image-unconfigured-host

こちら外部ドメインの画像を使用するとエラーが出てる模様なので、next.config.jsに下記のように使用するドメインを指定してあげる。

next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  swcMinify: true,
  images: {
    domains: ['images.dog.ceo'], //ここにドメインを指定
  },
}
module.exports = nextConfig

実際の流れ

今回はNext.jsを使用するが、作業ファイルはpages/index.jsで完結させる。
以下のような使用とする。

  • ページを表示したらAPIから画像のURLを取ってくる
  • クリックしたらAPIから画像URLを取ってきて画像を切り替える

全体のコード

pages/index.js
import Image from 'next/image'
import { useState } from 'react'
import styles from '../styles/Home.module.css'

const searchDog = async () => {
  const response = await fetch("https://dog.ceo/api/breeds/image/random");
  const res = await response.json();
  return res;
}

export default function Home({ initialCatImageUrl }) {
  const [catImage, setCatImage] = useState(initialCatImageUrl);

  const handleClick = async () => {
    const res = await searchDog();
    setCatImage(res.message);
  }

  return (
    <div className={styles.container}>
      <main>
        <h1>犬の画像</h1>
      </main>
      <div style={{ position: 'relative', width: 400, height:400 }}>
        <Image src={catImage} layout="fill" objectFit="contain" alt='犬' />
      </div>
      <button onClick={handleClick}>画像を検索</button>
    </div>
  )
}

export const getServerSideProps = async () => {
  const res = await searchDog();
  console.log(res);
  return {
    props: {
      initialCatImageUrl: res.message
    }
  };
};

htmlの表示部分

index.jsはHOMEとなってるかと思うが、ここにhtmlとしてブラウザに表示させる部分を記述する。他の処理はいったん省略。

pages/index.js
export default function Home({ initialCatImageUrl }) {
const [catImage, setCatImage] = useState(initialCatImageUrl);

  return (
  <div>
      <h1>猫の画像</h1> 
      <div style={{ position: 'relative', width: 400, height:400 }}>
        <Image src={catImage} layout="fill" objectFit="contain" alt='犬' />
      </div>
      <button onClick={handleClick}>画像を検索</button>
   </div>
  )
}

後ほど説明するが、HOMEの引数にある{ initialCatImageUrl }getServerSidePropsから受け取るプロパティ名である。

また、「最初にページを読み込んだ時」「ボタンをクリックした時」に画像のURLが変わるのでuseSatecatImageを管理します。初期値はgetServerSidePropsから受け取ったinitialCatImageUrlを設定しておきます。
クリックしたらhandleClickという関数を設定しておく。

APIから画像データを取ってくる

まずは、APIから画像データを取ってくる関数を処理。

pages/index.js
const searchDog = async () => {
  const response = await fetch("https://dog.ceo/api/breeds/image/random");
  const res = await response.json();
  return res;
}

クリックしたときと読み込み時と使いまわす必要があるので、APIから画像を取ってくる処理をsearchDogという関数にして取得したデータをreturnで返しておく。
こちらは非同期処理として、asyncとしてawaitで処理している。

またこちらはgetServerSidePropsでもserachDog関数を使う必要があるので、Homeの関数コンポーネントの外に記述しておく。

ボタンをクリックしたら呼び出す

ボタンをクリックしたらAPIから画像データを取ってくるsearchDog関数からデータを取ってくる。
もちろん非同期処理なのでhandleClickasyncを指定してawaitsearchDogを呼び出す。

その後、useSatesetCatImageres.messageで画像のURLをセットする。
messageとなってるが、こちらのAPIは画像のURLがmessageプロパティにある。

pages/index.js
 const handleClick = async () => {
    const res = await searchDog();
    setCatImage(res.message);
  }

getServerSidePropsで読み込み時にAPIからデータを取ってくる

getServerSidePropsを設定しない場合、クリックするまで画像が読み込まれないのでスペースができてしまう。
それを防ぐためにアクセスした際の処理として、getServerSidePropsを設定する。

pages/index.js
export const getServerSideProps = async () => {
  const res = await serachDog();
  return {
    props: {
      initialCatImageUrl: res.message
    }
  };
};

画像を読み込むためにgetServerSidePropsで読み込む際にsearchDog関数を実行して、propsinitialCatImageUrlというプロパティ名で画像のURLを設定してreturnで返してあげる。
この値を、HOMEの引数から受け取って、useStateの初期値として、設定してあげれば、Imageコンポーネントのsrc属性に設定したcatImageで画像が表示される。

まとめ

いったん、これでアクセス時に画像を読み込む処理とクリック時に読み込む処理は終わり。
あとは読み込み時にローディングなどuseStateでやってみるとかはまた今度。

Discussion