React:SWRで簡単にフェッチする

SWR とは

SWR(stale-while-revalidate) は、データ取得のための React Hooks ライブラリで、次の戦略で実装されている。

  1. キャッシュからデータを返す(stale)
  2. フェッチリクエストを送る(revalidate)
  3. 最新のデータを取得する

つまり、SWR を用いることで、コンポーネントはデータの更新を継続的かつ自動的に受け取ることができるようになる:

import useSWR from "swr";

function Profile() {
  const fetcher = (...args) => fetch(...args).then((res) => res.json());

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

  if (error) return <div>failed to load</div>;
  if (isLoading) return <div>loading...</div>;
  return <div>hello {data.name}!</div>;
}

useSWR フックは、 第一引数に『データの一意な識別子(通常、API の URL)』、第二引数に『データを返す任意の非同期関数』を設定する。第二引数には、ネイティブの fetchAxios のようなツールを設定できる。

userSWR フックの戻り値の data, error, isLoading からリクエストの状態("loading", "ready", "error") を判断し、対応する UI を返すことができる。

useSWR の API

API は次の構成になっている:

const { data, error, isLoading, isValidating, mutate } = useSWR(
  key,
  fetcher,
  options
);
  • 引数

    データ名 説明
    key リクエストのためのユニークなキー文字列(関数、配列、nullも可)
    fetcher データをフェッチするための Promise を返す関数
    options SWR の細かい設定を行うためのパラメータ。再検証の設定や通信諸設定、リクエスト後のコールバックなど設定できる。詳細
  • 返り値

    データ名 説明
    data fetcher によって解決された、key のデータ(ロードされてない場合 undefined
    error fetcher によって例外が投げられた際にキャッチされたデータ
    isLoading 実行中のリクエストが存在し『ロードされたデータがない』状態
    isValidating リクエスト、または再検証の読み込みがある状態
    mutate(data?,options?) キャッシュされたデータを更新する関数

コンテキストとして SWR を使う

SWR ではコンテキストとしてデータをグローバルに扱えるようにするために SWRConfig コンテキストが用意されている:

<SWRConfig value={options}>
  <Component />
</SWRConfig>

value には、useSWR の引数の option パラメータを設定できる:

import useSWR, { SWRConfig } from "swr";

function Dashboard() {
  const { data: events } = useSWR("/api/events");
  const { data: user } = useSWR("/api/user", { refreshInterval: 0 }); // SWRConfig の設定をオーバーライド
  // ...
}

function App() {
  return (
    <SWRConfig
      value={{
        refreshInterval: 3000,
        fetcher: (resource, init) =>
          fetch(resource, init).then((res) => res.json()),
      }}
    >
      <Dashboard />
    </SWRConfig>
  );
}

上記は、全ての SWR フックを同じフェッチャーを用いて更新するように設定している。

関数として利用する

useSWR で SWR を定義しない方法もある:

import { SWRConfig, useSWRConfig } from "swr";

function App() {
  return (
    <SWRConfig
      value={{
        refreshInterval: 100,
        fallback: { a: 1, b: 1 },
      }}
    >
      <Page />
    </SWRConfig>
  );
}

function Page() {
  const config = useSWRConfig();
}

上記は、コンテキストとして SWR を子コンポーネントに渡している。useSWRConfig は、グローバル設定(コンテキスト)および、ミューテーション、キャッシュを取得している。

自動再検証

自動再検証に関して次のようなオプションがある:

オプション 説明 デフォルト値
revalidateOnFocus ページ時にフォーカスを合わせるか、タブを切り替えると自動的にデータを再検証する(参考 有効
refreshInterval 画面上に表示されているフックに関連づけられたコンポーネントを時間の経過とともに更新する(参考 無効
refreshWhenHidden ウィンドウが非常時の時にポーリングする 無効
refreshWhenOffline ブラウザがオフラインの時にポーリングする 無効
revalidateOnReconnect ネットワークが回復したときに再検証する 有効
revalidateOnMount コンポーネントのマウント時に再検証する 未定義
revalidateIfStale 古いデータがある場合でも再検証する 有効

条件付きフェッチ

条件付きでデータを取得する場合には、key(第一引数)に null か関数を設定する。関数がスローまたは偽値を返した場合、SWR はリクエストを開始しない:

// 条件付きでフェッチする
const { data } = useSWR(shouldFetch ? '/api/data' : null, fetcher)
 
// user.id が定義されてない場合にスローする
const { data } = useSWR(() => '/api/data?uid=' + user.id, fetcher)

引数

デフォルトでは、keyfetcher に渡される。下記の式は同じ:

useSWR('/api/user', () => fetcher('/api/user'))
useSWR('/api/user', url => fetcher(url))
useSWR('/api/user', fetcher)

複数の引数を渡したい場合は、次のように指定する:

const { data: user } = useSWR(['/api/user', token], ([url, token]) => fetchWithToken(url, token))

オブジェクトを渡すこともできる:

const { data: user } = useSWR(['/api/user', token], fetchWithToken)
 
// 上で設定した user を使用する
const { data: orders } = useSWR(user ? ['/api/orders', user] : null, fetchWithUser)

参考