NotionAPI×Next.jsで、画像を表示する方法。useSWRを使用して画像のURLのみページ表示時にレンダリングする

NotionAPIで画像を取得すると、1時間で期限切れになるURLの問題に直面することになります。
みなさん様々な工夫をされていますが、useSWRを使用してこの問題を回避しました。

useSWRはデータのフェッチとキャッシュを自動的に管理するReactフックですが、これを使用することでページ全体はSSGにしながら画像のURL部分のみページ表示時にレンダリングすることができます。

import { Client } from "@notionhq/client";
import React from "react";
import { List, Card, Image, Spin, Pagination } from "antd";
import {
  BookOutlined,
  EditOutlined,
  LinkOutlined,
  IdcardOutlined,
  LoadingOutlined,
} from "@ant-design/icons";
import useSWR from "swr";
import axios from "axios";

const antIcon = (
  <LoadingOutlined
    style={{
      fontSize: 24,
    }}
    spin
  />
);

const fetcher = async (url) => {
  if (!url) return null;
  const response = await axios.get(url);
  if (response.data.type === "external") {
    return response.data.url;
  } else if (response.data.type === "file") {
    return response.data.file.url;
  }
  return null;
};

export async function getStaticProps() {
  const notion = new Client({ auth: process.env.NOTION_API_KEY });
  const databaseId = process.env.DATABASE_ID;
  const response = await notion.databases.query({
    database_id: databaseId,
  });
  const data = response.results.map((page) => {
    return {
      id: page.id,
      title: page.properties.title.title[0].text.content,
      author: page.properties.author.rich_text[0]?.text?.content || "",
      text: page.properties.action.rich_text[0]
        ? page.properties.action.rich_text[0].plain_text
        : "",
      thumbnail:
        page.properties.thumb && page.properties.thumb.files.length > 0
          ? page.properties.thumb.files[0].file.url
          : null,
    };
  });

  return {
    props: { data },
  };
}

export default function Home({ data }) {
  const { data: imageUrl, error } = useSWR(data?.thumbnail, fetcher);
  return (
    <div className="container py-10 mx-auto px-4 transition-all">
      <h1 className="text-3xl mb-10 font-bold underline-offset-8 underline">
        PageTitle
      </h1>
      <div
        id="scrollableDiv"
        className="rounded border-solid border-2 border-black-600 shadow-md px-5"
      >
        <List size="large" itemLayout="vertical">
          {data.map((page, index) => (
            <List.Item key={page.id}>
              <div className="flex gap-x-5">
                <div className="min-w-[200px] relative">
                  <Image
                    // preview={false}
                    width={200}
                    alt={page.title}
                    src={page.thumbnail}
                    fallback="https://placehold.jp/200x250.png"
                  />
                </div>
                <div className="flex-1">
                  <div className="text-lg font-bold mb-5 flex items-start gap-x-1">
                    <BookOutlined className="mt-1" />
                    <p>{page.title}</p>
                  </div>

                  <div className="space-y-1">
                    <p className="text-md flex gap-x-1 text-gray-600">
                      <EditOutlined className="mt-1" />
                      {page.author}
                    </p>
                    <p className="text-sm text-gray-600 flex gap-x-1">
                      <IdcardOutlined className="mt-1" />
                      {page.id}
                    </p>
                    <p className="text-sm text-gray-600 flex gap-x-1">
                      <LinkOutlined className="mt-1" />
                      ImageURL:{imageUrl ? imageUrl[index] : page.thumbnail}
                    </p>
                  </div>
                </div>
              </div>
            </List.Item>
          ))}
        </List>
      </div>
    </div>
  );
}