Oshiro-lab.

SWR に入門してみる

2023-03-15

今回は Next.js + SWR のよくある構成で、公式のドキュメントに沿って使ってみます。

加えて Next.js の機能である API Routes も組み合わせてみます。

Next.js アプリを作成

まずは Next.js アプリを作成。

npx create-next-app sample-app --typescript

SWR をインストール

yarn add swr

API Routes で API 作成

API Routes で API を作成する手順はシンプルで、pages/api フォルダ配下にファイルを作成することで、自動的に /api/* にマッピングされます。

例えば pages/api/user.ts というファイルを作成すると /api/user というエンドポイントを作成。

// pages/api/user.ts
import { NextApiRequest, NextApiResponse } from 'next';

const user = (req: NextApiRequest, res: NextApiResponse) => {
  res.status(200).json({
    name: 'Tatsuya Oshiro',
    twitter: '@oshiroman2'
  });
}

export default user;

ローカルサーバを立ち上げ http://localhost:3000/api/user にアクセスしてみると、上記のコードで返しているパラメータを確認できます。

SWR を導入する

import useSWR from 'swr';

default export なので任意の名前を指定できますが、useSWR とするのが分かりやすい(公式ドキュメントもそうしている)です。

const { data, error, isLoading } = useSWR('/api/user', fetcher);

useSWR の第 1 引数には先程作成した API Routes のエンドポイント、第 2 引数には fetcher を指定します(後述)。

fetcher 経由で取得したデータは data、リクエストに何らかの理由でエラーとなった場合は error、リクエスト処理中は isLoading にそれぞれ値が入ります。

fetcher

fetch や Axios など、いくつかやり方はありますが、ここでは公式に沿って fetch で fetcher を作成します。

const fetcher = async (path: string) => {
  const res = await fetch(path);
  const data = await res.json();

  return data;
}

ここで作成した fetcher を useSWR の第 2 引数に指定すると、useSWR の第 1 引数に渡しているパスを fetcher の引数として受け取ることができます。

今回の例では /api/user をパスとして渡します。

コード

// pages/index.tsx
import { NextPage } from 'next'
import useSWR from 'swr';

const fetcher = async (path: string) => {
  const res = await fetch(path);
  const data = await res.json();

  return data;
}

const IndexPage: NextPage = () => {
  const { data, isLoading } = useSWR('/api/user', fetcher);

  if (error) {
    return <p>エラーが発生しました。</p>
  }

  if (isLoading) {
    return <p>Now loading...</p>
  }

  return (
    <div style={{ margin: 32, fontSize: 32 }}>
      <p>Name: {data.name}</p>
      <p>Twitter: {data.twitter}</p>
    </div>
  );
}

export default IndexPage;

これまでの内容をまとめると上記のようなコードになります。

従来の React Hooks では useState と useEffect を組み合わせて、自分自身でローディングの状態管理やエラーハンドリングをする必要がありましたが、

上記のコードのように useSWR だけで、煩わしいコードがまるっと削減されます。

画面

image

SWR の再利用

カスタムフックと同じ要領で useUser という名前でデータフックを作成します。

// hooks/useUser.ts
import useSWR from 'swr';

const fetcher = async (path: string) => {
  const res = await fetch(path);
  const data = await res.json();

  return data;
}

const useUser = () => {
  const { data, error, isLoading } = useSWR(`/api/user`, fetcher);

  return {
    user: data,
    isLoading,
    isError: error,
  };
}

export default useUser;

作成した useUser をコンポーネント側で呼び出します。

// pages/index.tsx
import { NextPage } from 'next'
import useUser from '../hooks/useUsers';

const IndexPage: NextPage = () => {
  const { user, isLoading, isError } = useUser();

  if (isLoading) {
    return <p>Now loading...</p>
  }

  if (isError) {
    return <p>An error occurred.</p>
  }

  return (
    <div style={{ margin: 32, fontSize: 32 }}>
      <p>Name: {user.name}</p>
      <p>Twitter: {user.twitter}</p>
    </div>
  );
}

export default IndexPage;

このようにすることで、ユーザ情報 (user) を他の画面やコンポーネントでも再利用できるようになります。

useSWR の第 1 引数に渡しているパラメータ (SWR キー) が同一であれば、何度もリクエストを送らずにキャッシュからデータを取得するため、

パフォーマンスの観点からも非常に優秀です。

今回はとりあえず使ってみるだけにフォーカスを当てたので、

次は、本サイトでも導入している Next.js + SWR + microCMS 構成の実例を紹介します。

(c) 2015 - 2024 Tatsuya Oshiro