データ取得のための React Hooks ライブラリSWRの使い方

2024/04/18に公開

SWRとは

Next.jsを開発しているVercelによって提供されているReactベースのデータ取得のためのライブラリです。
https://swr.vercel.app/ja

下記の3つ点で便利かなと思います。

  • キャッシュを先に利用し、その後で最新のデータに更新するため、ユーザーに対してより速いレスポンスを提供できます。これにより、アプリケーションのパフォーマンスが向上します。

  • 定期的にバックエンドにリクエストを送り、データが更新されていればそれを反映します。これにより、ユーザーにほぼリアルタイムのデータを提供することができます。

  • ローディング状態やエラー状態を簡単に扱えるフックを提供しているので、それらの状態を簡単に管理できます。

MSWとSWRを組み合わせたデータ取得について下記の記事で書いているので、この記事を読んでから見ていただくとイメージしやすいと思います。
https://zenn.dev/nenenemo/articles/71e29812101e5b

インストール

下記コマンドでswrライブラリをインストールしてください。

npm install swr

使用例

フェッチしたいデータに対してuseSWRフックを使用します。

下記の例では、/api/user[1]からユーザー情報をフェッチしています。useSWRフックは、データ(data)、ローディング状態、エラー情報を返します。これにより、コンポーネント内でデータのローディングやエラーハンドリングを簡単に実装できます。

src/app/user/page.tsx
import useSWR from 'swr'

export default function User() {
  const fetcher = (url: string) => fetch(url).then(res => res.json());
  const fetcher = (url: string) => axios.get(url).then((res) => res.data);

  const { data: userData, error: userError, isValidating, mutate} = useSWR('/api/user', fetcher, {
    revalidateOnFocus: false,
    shouldRetryOnError: false,
    revalidateOnMount: false,
  })
  if (userError) return <div>Failed to load</div>
  if (!userData) return <div>Loading...</div>
  return <div>Hello, {userData.name}!</div>
}

const { data: userData, error: userError, isValidating, mutate} = useSWR('/api/user', fetcher, { revalidateOnFocus: false, shouldRetryOnError: false, revalidateOnMount: false, })
の部分で、data: userDataとしているのは、useSWRから返されるdataオブジェクトにuserDataという新しい名前を割り当てているだけです。

返されるデータをそのままdataとして扱いたい場合は、以下のように記述してください。

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

useSWR

useSWRは、データをフェッチ(取得)し、ローカルのキャッシュに保存して自動的に再検証(更新)する機能を提供するSWRライブラリの一部です。
https://swr.vercel.app/ja/docs/typescript.ja#useswr

オプション設定

revalidateOnFocus

ページがフォーカスされた際に自動的に再検証を行うか設定できます。

shouldRetryOnError

エラーが発生した場合の再試行を行うか設定できます。

revalidateOnMount

コンポーネントがマウントされた際に再検証を行うか設定できます。

返り値

data

フェッチされたデータが含まれます。取得に成功した場合はデータがセットされ、失敗した場合は null になります。

error

フェッチ中またはフェッチ失敗時に発生したエラーが含まれます。

isValidating

現在データを再検証しているかどうかを示します。再検証中はtrueになります。

mutate

データを再フェッチするための関数です。データを強制的に再取得したい場合に使用します。

axiosを使用して、POSTリクエストを送信する

awaitを使用して、axios.postmutateの非同期処理が成功したことを確認してから次のステップに進むように記述しています。

try/catchブロックを使用して、エラー時の処理を追加しています。

src/app/page.tsx
import axios from 'axios';
import { useRouter } from 'next/navigation';
import { mutate } from 'swr';

export default function Home() {
  const router = useRouter(); 

  const data = {
    name: 'Mike',
    age: 30,
    occupation: 'engineer',
  };

const nextOnClick = async () => {
  try {
    await axios.post<string>('/user', data);
    await mutate('/user');
    await router.push('/user');
  } catch (error) {
    console.error('An error occurred:', error);
  }
};

return (
    <main>
      <button onClick={nextOnClick}>クリックして次へ</button>
    </main>
  );
}

fetchを使用して、POSTリクエストを送信する

先ほどのaxiosを使用する方法と比較すると、ヘッダーやリクエストボディを手動で設定する必要がありますが追加のライブラリを必要としません。

src/app/page.tsx
import { useRouter } from 'next/navigation'; 
import { mutate } from 'swr';

export default function Home() {
  const router = useRouter(); 
  const data = {
    name: 'Mike',
    age: 30,
    occupation: 'engineer',
  };

  const nextOnClick = async () => {
    try {
      const response = await fetch('/user', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json', 
        },
        body: JSON.stringify(data), 
      });

      if (!response.ok) {
        throw new Error('Request failed'); 
      }

      await mutate('/user');
      await router.push('/user');
    } catch (error) {
      console.error('An error occurred:', error); 
    }
  };

return (
    <main>
      <button onClick={nextOnClick}>クリックして次へ</button>
    </main>
  );
}

mutate

mutate関数を使うと、ウェブアプリケーションがサーバーから取得したデータを更新、再検証することができます。これは主に、データが変更された後にフロントエンドの表示をすぐに更新したい場合に役立ちます。
https://swr.vercel.app/ja/docs/mutation

mutateを使用する際は、通常GETリクエストで取得するデータのキャッシュを更新するために使います。

更新

ユーザーが何らかのアクション(例えば、プロフィール名の編集やコメントの投稿など)を行い、その変更をサーバーに送信した後、フロントエンドで表示されているデータを即座に新しい状態に更新します。

この時、実際にサーバーから新しいデータを取得する前に、フロントエンドの表示だけを先に更新することができます。これにより、ユーザーは自分の行った変更がすぐに反映されたように感じることができます。これはオプティミスティックUI[2]更新といいます。

再検証

更新後、あるいは任意のタイミングで、mutate関数を使って特定のキーに対応するデータを再検証します。つまり、サーバーに再度問い合わせて最新のデータを取得し、フロントエンドの表示をサーバーのデータと同期させます。

このプロセスはバックグラウンドで自動的に行われるため、ユーザー体験に影響を与えることなく、アプリケーションのデータを常に最新の状態に保つことができます。

終わりに

何かありましたらお気軽にコメント等いただけると助かります。
ここまでお読みいただきありがとうございます🎉

脚注
  1. https://zenn.dev/nenenemo/articles/59ca1b03fcf234 ↩︎

  2. https://zenn.dev/funteractiveinc/articles/optimistic-update ↩︎

Discussion