Next/Icremental Static Regeneration

pages/isr/index.tsx
  import Head from 'next/head';
import { GetStaticProps, GetStaticPropsContext } from 'next';
import Link from 'next/link';

// pages/isg/index.tsxのコードと共通している部分が多いが、
// 解説が1ファイルで完結できるようにあえて
// 別ファイルに用意しない形で記述している。
//
// また、
// この中身のファイルは実質pages/isr/index.tsxと同じで、
// ISR解説用のサンプルコードのメインはpages/isr/posts/[id].tsxとなる

type Post = {
  userId: number;
  id: number;
  title: string;
  body: string;
};

interface ISRProps {
  posts: Post[];
}

export default function ISR({ posts }: ISRProps) {
  return (
    div>
      Head>
        title>ISRの解説用ページ(Postリンク一覧)/title>
        link rel="icon" href="/favicon.ico" />
      /Head>

      main>
        h1>Postのリンク一覧/h1>
        ul>
          {posts.map((post) => {
            return (
              li key={post.id}>
                Link
                  // https://nextjs.org/docs/api-reference/next/link
                  // "next/link" はデフォルトでリンク先のデータの取得(prefetch)やISGを実行させてしまう
                  // ISGの動作確認がわかりやすいように、ここではprefetchの機能をオフにしている
                  prefetch={false}
                  href={`/isr/posts/${post.id}`}
                >
                  {post.title}
                /Link>
              /li>
            );
          })}
        /ul>
      /main>
    /div>
  );
}

export const getStaticProps: GetStaticProps = async (
  _context: GetStaticPropsContext
) => {
  const res = await fetch('https://jsonplaceholder.typicode.com/posts');
  const posts = (await res.json()) as Post[];

  return {
    props: {
      posts,
    },
  };
};
	
pages/isr/posts/[id].tsx
  import Head from 'next/head';
import {
  GetStaticProps,
  GetStaticPropsContext,
  GetStaticPaths,
  GetStaticPathsContext,
} from 'next';

// pages/isg/posts/[id].tsxと共通するコードが複数あるが
// 解説が1ファイルで完結できるようにあえて
// 別ファイルに用意しない形で記述している

type Post = {
  userId: number;
  id: number;
  title: string;
  body: string;
};

interface ISRProps {
  post: Post;
  timestamp: number; // ISR確認用のタイムスタンプ
}

export default function ISRPostsId({ post, timestamp }: ISRProps) {
  return (
    div>
      Head>
        title>ISRの解説用ページ(Post詳細)/title>
        link rel="icon" href="/favicon.ico" />
      /Head>

      main>
        h1>Post詳細(ISR)/h1>
        div>
          p>Post ID: {post?.id}/p>
          p>User ID: {post?.userId}/p>
          p>Title: {post?.title}/p>
          p>Body: {post?.body}/p>
          p>ISR確認用のtimestamp値: {timestamp}/p>
        /div>
      /main>
    /div>
  );
}

type ISRParams = {
  id: string;
};
export const getStaticPaths: GetStaticPaths = async (
  _context: GetStaticPathsContext
) => {
  return {
    paths: [{ params: { id: '1' } }],
    fallback: true, // or 'blocking',
  };
};

export const getStaticProps: GetStaticProps = async (
  context: GetStaticPropsContext
) => {
  const params = context.params as ISRParams;
  const postId = params.id;
  const res = await fetch(
    `https://jsonplaceholder.typicode.com/posts/${postId}`
  );
  const post = (await res.json()) as Post;

  return {
    props: {
      post,
      timestamp: Date.now(),
    },

    /**
     * https://nextjs.org/docs/basic-features/data-fetching#incremental-static-regeneration
     *
     * SSG、もしくはISGでHTMLファイルを生成済みだとしても、
     * revalidateに指定した秒数が経過したあとに再度アクセスがあったときに、
     * HTMLファイルの中身を現在の情報を元に更新をする。
     *
     * revalidateに3をセットした場合の再生成の流れ
     *
     * 1. HTMLファイルが生成された3秒経過するまでは、どんなにアクセスがあっても生成済みのHTMLを返す
     * 2. 3秒経過後の初回アクセスでは、クライアントには現時点での生成済みのHTMLを返しつつ、サーバー側では再生成の処理が行われる
     * 3. HTMLが再生成されたあとは(3秒経過後の2回目以降のアクセス)、再生成後のHTMLがクライアントに返される
     * 4. 1, 2, 3を繰り返す
     */
    revalidate: 3,
    }
}