データ取得のための React Hooks ライブラリ「SWR」をNext.jsで試してみた

2022.01.12

こんにちは!DA(データアナリティクス)事業本部 サービスソリューション部の大高です。

Next.jsと同じチームによって作成されている、データ取得のための React Hooks ライブラリとして「SWR」というライブラリがあります。

今回はこのライブラリの一番基本の部分をNext.jsのプロジェクトで試してみたいと思います。

SWRとは

公式サイトにも以下のように記載されていますが「SWR」という名前はHTTPキャッシュ無効化戦略であるstale-while-revalidateに由来しています。

“SWR” という名前は、 HTTP RFC 5861 で提唱された HTTP キャッシュ無効化戦略である stale-while-revalidate に由来しています。 SWR は、まずキャッシュからデータを返し(stale)、次にフェッチリクエストを送り(revalidate)、最後に最新のデータを持ってくるという戦略です。

この考え方の仕組みにより、データが高速に返却され、更に自動で再フェッチ(revalidate)されるようになっています。

データの自動再フェッチ(revalidate)に関しては下記のページに詳細が記載されていますが、とても良いですね!

他にも、以下のような特徴を備えています。

  • Next.js の SSR / ISR / SSG サポート
  • TypeScript 対応
  • React Native 対応

実際に試してみる

では、実際に公式のGetting Startedを見ながら試してみたいと思います。

対象プロジェクトの作成

まずはNext.jsのプロジェクトを作成しておきます。プロジェクト名はhello-swrとしました。

% yarn create next-app --typescript
yarn create v1.22.17
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 🔨  Building fresh packages...

success Installed "create-next-app@12.0.7" with binaries:
      - create-next-app
✔ What is your project named? … hello-swr

インストール

プロジェクトhello-swrを作成したら、ディレクトリ移動をしてSWRをインストールします。

% cd hello-swr
% yarn add swr
yarn add v1.22.17
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
warning "next > styled-jsx > @babel/plugin-syntax-jsx@7.14.5" has unmet peer dependency "@babel/core@^7.0.0-0".
[4/4] 🔨  Building fresh packages...
success Saved lockfile.
success Saved 1 new dependency.
info Direct dependencies
└─ swr@1.1.2
info All dependencies
└─ swr@1.1.2
✨  Done in 1.08s.

サンプル作成

インストールしたら、 公式ドキュメントのサンプル「基本的な使用法」を参考に、index.tsx を以下のように書き換えてサンプルを作成してみます。

pages/index.tsx

import type { NextPage } from "next";
import useSWR from "swr";
import styles from "../styles/Home.module.css";

const fetcher = (url: string) => fetch(url).then((res) => res.json());

const Home: NextPage = () => {
  const { data, error } = useSWR(
    "https://api.github.com/repos/vercel/swr",
    fetcher
  );

  if (error) return <div>An error has occurred.</div>;
  if (!data) return <div>Loading...</div>;

  return (
    <div className={styles.container}>
      <main className={styles.main}>
        <h1 className={styles.title}>{data.name}</h1>
        <p className={styles.description}>{data.description}</p>
        <p>
          <strong>👁 {data.subscribers_count}</strong>{" "}
          <strong>✨ {data.stargazers_count}</strong>{" "}
          <strong>🍴 {data.forks_count}</strong>
        </p>
      </main>
    </div>
  );
};

export default Home;

このサンプルではhttps://api.github.com/repos/vercel/swrにリクエストした結果のJSON情報を画面に表示しています。また、リクエスト中にはLoading...、リクエストエラーの場合にはAn error has occurred.と表示されるようになっています。

実際に実行すると、このような感じに表示されます。

APIレスポンスが早く、パッと見た感じでは通常のAPI呼び出しと違いが分かりづらいので、少しコードを変えてみます。

pages/index.tsx

import type { NextPage } from "next";
import useSWR from "swr";
import styles from "../styles/Home.module.css";

function sleep(msec: number) {
  return new Promise((resolve) => {
    setTimeout(resolve, msec);
  });
}

const fetcher = (url: string) =>
  fetch(url).then(async (res) => {
    sleep(5000);
    return res.json();
  });

const Home: NextPage = () => {
  const { data, error } = useSWR(
    "https://api.github.com/repos/vercel/swr",
    fetcher
  );

  if (error) return <div>An error has occurred.</div>;
  if (!data) return <div>Loading...</div>;

  return (
    <div className={styles.container}>
      <main className={styles.main}>
        <h1 className={styles.title}>{data.name}</h1>
        <p className={styles.description}>{data.description}</p>
        <p>
          <strong>👁 {data.subscribers_count}</strong>{" "}
          <strong>✨ {data.stargazers_count}</strong>{" "}
          <strong>🍴 {data.forks_count}</strong>
        </p>
      </main>
    </div>
  );
};

export default Home;

今度は、リクエスト時に5秒ほど待ってからレスポンスを返すようにしてみました。

こちらを試すと、最初の5秒は以下のようにローディング表示がされ、

その後に以下のように表示が自動で切り替わります。

また、このデータがキャッシュされるのでリロードしてもリクエスト結果が変わらない限り、初期表示としてこの画面が即時表示されるようになります。(毎回5秒を待つことはありません)

まとめ

以上、データ取得のための React Hooks ライブラリ「SWR」をNext.jsで試してみました。

今回は一番基本の部分だけでしたが、これだけでもコードの書き方としてかなり楽になると感じました。また、キャッシュ無効化戦略に基づいた仕組みもよく、高速に描画ができるのが良いなと思いました。今後、もう少し別のオプションなども含めて試してみたいと思います。

どなたかのお役に立てば幸いです。それでは!

参考