thumbnail

SWRとは?

2021/12/12

SWRは、NextJSを作っているVercel社が開発したReact Hooksライブラリであり、データ取得、管理をstale-while-revalidateという方針に従って行ってくれるライブラリになります。

stale-while-revalidateは、HTTP RFC 5861で提唱された HTTP キャッシュ無効化戦略です。

stale-while-revalidateに従ってデータ取得を行う場合、既存のキャッシュを返して、その後にバックグラウンドでデータ取得を行います。

得られたデータと既存のキャッシュに違いがないかを検証し、違いがあった場合はすぐにキャッシュに反映するので、可能な限りキャッシュは最新の状態に保たれるような仕組みになっています。

より詳細を知りたい場合は公式サイトがよくまとめられているので、そちらをご参考ください。

使用方法

簡単に使用方法を紹介します。 必要なライブラリはswrだけです。

shell
yarn add swr

swrは取得してきたデータをいい感じにキャッシュ管理するライブラリなので、データ取得部分(fetcher)は各自で用意します。

fetcher.ts
const fetcher = (...args) => fetch(...args).then(res => res.json())

※ 上記ではネイティブのfetchをラップしたfetcher関数を作成していますが、fetcherにはaxiosGrahQL APIも使えます。

あとはuseSWR をインポートして、任意の関数コンポーネント内で使用するだけです。

import useSWR from 'swr'

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

  if (error) return <div>failed to load</div>
  if (!data) return <div>loading...</div>

  // データをレンダリングする
  return <div>hello {data.name}!</div>
}

SWRを使うメリット

SWRを使うと何が嬉しいのか、個人的に良いと思った機能をまとめてみました。

シンプルなキャッシュ機能

Reduxとかだと、取得してきたデータを明示的にstateに突っ込まなくてはいけませんが、useSWRではデータを取得すると勝手にいい感じでキャッシュ管理してくれます。

どちらが良いとかはありませんが、Redux抜きでシンプルにフロントのstateが管理できるのはswrのメリットの一つだと思います。

swrはurlに応じてデータをキャッシュしているのでどのコンポーネントからでもuseSWRで同じurlを指定すれば、簡単にキャッシュの取得が可能です。

例えば、以下のようなcustom hooksを用意して、

useUsers.ts
function useUsers() {
  const { data, error } = useSWR(`/api/users`, fetcher)

  return {
    users: data,
    isLoading: !error && !data,
    isError: error
  }
}

別々のコンポーネントからそれぞれuseUsersを使えば、同じusersのキャッシュを取得できます。

components.tsx
// ページコンポーネント
const Page: React.FC = () => {
  return <div>
    <Navbar />
    <Content />
  </div>
}

// 子コンポーネント
const Navbar: React.FC = () => {
  return <div>
    ...
    <Avatar />
  </div>
}

const Content: React.FC = () => {
  const { users, isLoading } = useUsers()
  if (isLoading) return <Spinner />
  return <h1>Welcome back, {user.name}</h1>
}

const Avatar: React.FC = () => {
  const { users, isLoading } = useUsers()
  if (isLoading) return <Spinner />
  return <img src={user.avatar} alt={user.name} />
}

リクエストの重複排除

上記の例だとusers取得のリクエストが複数回行われそうですが、useSWRを使っている場合はAPIへのリクエストは1回になります。

APIとの無駄な通信を気にする必要なく、useSWRを使ってキャッシュへ手軽にアクセスできるのは地味に便利です。

自動再検証機能(ポーリングなど)

フォーカス時の再検証 公式にある自動再検証の動画がわかりやすいので引用させていただきます。

↓はフォーカス時の再検証を使用して、ページ間でのログイン状態を自動的に同期している様子です。

SWRはブラウザのwindowやtabフォーカス時に、裏側でapiと通信をしキャッシュの更新を自動で行います。 したがって、フォーカスした時点でのフロントは常に最新の状態に保たれます。

ポーリング 掲示板などのリアルタイム更新が必要なアプリでは、WebSocketやポーリングなどの技術が必要になってきます。

WebSocketはサーバー側の実装も大変なので、簡単にリアルタイム更新を実装しようと思うとhttp通信だけで済むポーリングを使用すると思います。

SWRではそのポーリング(定期的な自動再検証)を難しい設定なしで簡単に利用できます。

方法は以下のようにuseSWRを使用する際に、refreshIntervalのoptionを指定するだけです。

useSWR('/api/todos', fetcher, { refreshInterval: 1000 })

この場合は1000ms間隔でポーリングが行われます。 ↓動画のようなイメージです。

機能としてはシンプルなポーリングですが、画面には表示されていないコンポーネントが裏側で常に通信を行ってしまう可能性に注意しなければなりません。

今までのポーリングはそこらへんを気をつけながら実装するのが面倒だったのですが、SWRではフックに関連付けられているコンポーネントが画面に表示されている場合にのみ通信を行う仕様になっています。

とにかく自動再検証を行う上で無駄なリクエストはできるだけ行わないという意思を感じます 😊

自動再検証の無効化 ここまで紹介した自動再検証機能はなくてもいい!っていう場合もあると思うので、その場合はuseSWRの代わりにuseSWRImmutableを使えばOKです 👌

import useSWRImmutable from 'swr/immutable'

// ...
useSWRImmutable(key, fetcher, options)

適切なエラーハンドリング

個人的に良いと思ったその他の機能としては、リクエストエラー時に適切なアルゴリズムで再試行をしてくれる点が挙げられます。

具体的にはエラー時に、SWRExponential backoffというアルゴリズムに従って、リクエストを自動再試行してくれます。

これにより、アプリはエラーから素早く回復することが可能となるだけではなく、再試行のしすぎでリソースを無駄にすることはありません。

このようなエラーハンドリングを自前で実装しようと思うと結構面倒くさいので、最初からライブラリに含まれているのはかなり良いと思いました 🤩

もちろん、Exponential backoffアルゴリズムに則ったエラーハンドリングを以下のようにオーバーライドすることも可能です。

useSWR('/api/user', fetcher, {
  onErrorRetry: (error, key, config, revalidate, { retryCount }) => {
    // 404では再試行しない。
    if (error.status === 404) return

    // 特定のキーでは再試行しない。
    if (key === '/api/user') return

    // 再試行は10回までしかできません。
    if (retryCount >= 10) return

    // 5秒後に再試行します。
    setTimeout(() => revalidate({ retryCount }), 5000)
  }
})

※上記の例では各エンドポイントごとにエラーハンドリングを設定していますが、グローバルにエラーハンドリングの設定を行うことも可能です。

その他のメリット

個人的に良いと感じたその他のメリットとしては以下が挙げられます。

  • ライブラリが軽量
  • typescript対応

ここで紹介した機能以外にも細かい機能がたくさんあるので、詳しくは公式サイトをご参考ください。

おわり

シンプルだけど、かなり実用的 🤩

author picture

Mitsuru Takahashi

京都市内にてフリーランスエンジニアとして活動しています。

detail

Profile

author picture

Mitsuru Takahashi

京都市内にてフリーランスエンジニアとして活動しています。

detail

© 2022 mitsuru takahashi