今回は Next.js + SWR のよくある構成で、公式のドキュメントに沿って使ってみます。
加えて Next.js の機能である API Routes も組み合わせてみます。
まずは Next.js アプリを作成。
npx create-next-app sample-app --typescript
yarn add swr
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 にアクセスしてみると、上記のコードで返しているパラメータを確認できます。
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 にそれぞれ値が入ります。
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 だけで、煩わしいコードがまるっと削減されます。
カスタムフックと同じ要領で 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 構成の実例を紹介します。