データ取得に役立つSWR

## SWRとは
Next.jsを提供しているVercel社が開発しているデータ取得用のReact Hooksライブラリです。命名はHTTPキャッシュ無効化戦略である[stale-while-revalidate](https://datatracker.ietf.org/doc/html/rfc5861)に由来します。

## SWRの特徴
- [Jamstack](https://qiita.com/ozaki25/items/4075d03278d1fb51cc37)指向
- 高速且つ軽量で再利用可能なデータ取得
- リアクティブな動作の実現
- 重複リクエストの排除
- 複雑なロジックの単純化

## 使い方
```react
import useSWR from 'swr';

const fetcher = (url: string): Promise<any> => fetch(url).then(res => res.json());

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>;
};
```

なんてことでしょう。シンプル過ぎてわからない。
これでは仕様がわかりづらいと思いますので、簡単に解説します。

### 引数
```typescript
// 定型
const { data, error } = useSWR(key, fetcher, option);

// 実例
const { data, error } = useSWR('/api/user/123', fetcher, { refreshInterval: 3000, revalidateOnReconnect: true });
```

- 第一引数
    - このリクエストのためのユニークなキー文字列（または関数、配列、null）
    - 主にリクエスト先のURLを指定する
- 第二引数
    - fetcher([Fetch API](https://developer.mozilla.org/ja/docs/Web/API/Fetch_API)等のデータ取得してPromiseを返却する関数)
    - GETメソッド且つFetch APIを利用出来る環境であれば、省略可能
- 第三引数
    - useSWRのオプション

### 返り値
```typescript
const { data, error, isValidating, mutate } = useSWR('/api/user/123', fetcher);
```

- data
    - 取得したデータ、もしくはundefined(データ取得中、もしくはデータ取得できなかった場合)
- error
    - データ取得時のエラー、もしくはundefined(データ取得中、もしくはエラー発生しなかった場合)
- isValidating
    - ローディング中(リクエスト中)にtrueとなるboolean型の変数
    - 例えば、ローディング中に実行したい処理がある場合に利用すると便利です。
- mutate 
    - キャッシュされたデータを更新する関数
    - 例えば、useSWRで取得したデータが何らかの方法で更新された場合、そのままでは取得時のキャッシュは更新されません。もちろん再取得すれば更新されますが、mutateを使えば再取得を待たずしてキャッシュの内容を更新することができます。

ちなみにdata及びエラーはPromiseを解決した値が返却されます。
このように複数の返り値を持ちますが、不要な返り値を受け取ると無駄な再レンダリングが発生する可能性がありますので、必要な値だけを受け取るようにしましょう。


### オプション
| 名称 | 効果 | 型 | デフォルト値 |
|:-----|:-----|:-----|:-----|
|suspense|[React Suspenseモード](https://swr.vercel.app/docs/suspense)を有効にする|boolean|false|
|initialData|データの初期値を設定します|any|undefined|
|revalidateOnMount|コンポーネントがマウントした時に自動的に再検証する|boolean|initialData が設定されてない場合、true|
|revalidateOnFocus|ウィンドウがフォーカスされた時に自動的に再検証する|boolean|true|
|revalidateOnReconnect|ブラウザがネットワーク接続を回復すると自動的に再検証する(navigator.onLine経由)|boolean|true|
|refreshInterval|ポーリングする時間間隔|number|0(無効)|
|refreshWhenHidden|ウィンドウが非表示の場合にポーリングする（refreshIntervalが有効になっているときのみ）|boolean|false|
|refreshWhenOffline|ブラウザがオフラインのときにポーリングする（navigator.onLineによって決定される）|boolean|false|
|shouldRetryOnError|フェッチャーでエラーが発生したときに再試行する|boolean|false|
|dedupingInterval|この期間内での同じキーのリクエストを重複として排除する|number|2000|
|focusThrottleInterval|この期間中に一度だけ再検証する|number|5000|
|loadingTimeout|`onLoadingSlow`イベントをトリガーするためのタイムアウト|number|3000|
|errorRetryInterval|エラー再試行の時間間隔|number|5000|
|errorRetryCount|最大エラー再試行回数|number|[exponential backoffアルゴリズム](https://en.wikipedia.org/wiki/Exponential_backoff)を使用してエラーの再試行を処理|
|onLoadingSlow(key, config)|リクエストの読み込みに時間がかかりすぎる場合のコールバック関数|(key: string, config: SWROptions) => void|undefined|
|onSuccess(data, key, config)|リクエストが正常に終了したときのコールバック関数|(data: any, key: string, config: SWROptions) => void|undefined|
|onError(err, key, config)|リクエストがエラーを返したときのコールバック関数|(error: any, key: string, config: SWROptions) => void|undefined|
|onErrorRetry(err, key, config, revalidate, revalidateOps)|エラー時の再試行をするコールバック|(error: any, key: string, config: SWROptions, revalidate: (options: RevalidateOptions) => Promise\<boolean>, revalidateOptions:{ dedupe: boolean; retryCount: number; }) => void|undefined|
|compare(a, b)|誤った再レンダリングを回避する返却データ更新時系列を検出するための比較関数|(a: any, b: any) => boolean|[dequal](https://github.com/lukeed/dequal)を使用|
|isPaused()|再検証を一時停止するかどうかを検出する関数|() => boolean|() => false|

※refreshWhenHiddenはrefreshIntervalが有効になっている場合に動作します。trueに設定する時は必ずrefreshIntervalを設定してください。
※isPausedがtrueを返す場合、再検証を停止して取得したデータとエラーを無視する

### オプションのグローバル設定
上記のオプションをuseSWRを利用する度に記述するのは少し面倒です。useSWRを利用する上で共通して設定したいオプションは以下のようにSWRConfigに記述することで設定できます。

```react
import useSWR, { SWRConfig } from 'swr'

function Dashboard () {
  const { data: events } = useSWR('/api/events')
  const { data: projects } = useSWR('/api/projects')
  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くん
Reactベースの開発を行っている場合は学習コストが低いと思いますので、一度採用を検討してみてはいかがでしょうか。

## 参考URL
[SWR データ取得のためのReact Hooksライブラリ](https://swr.vercel.app/ja)
[SWRはHTTPキャッシュ無効化戦略の夢を見るのか? - Zenn](https://zenn.dev/uttk/scraps/72f9ad74853fcc)