useSWR備忘録

## 参考対象

- useSWRって何？という方
- データ取得にuseEffectを使用している方

:::note info
インフォメーション
こちらは初歩的な内容になっております。もっと知りたい方は公式ドキュメント等をご覧ください。
:::

## useSWRとは

公式ドキュメントから引用します。

> “SWR” という名前は、 HTTP RFC 5861(opens in a new tab) で提唱された HTTP キャッシュ無効化戦略である stale-while-revalidate に由来しています。 SWR は、まずキャッシュからデータを返し（stale）、次にフェッチリクエストを送り（revalidate）、最後に最新のデータを持ってくるという戦略です。

つまり、useSWRは特定のデータをキャッシュし、必要に応じてそのデータを返しつつ、バックグラウンドで新しいデータを取得して最新状態を保ちます。

## useEffectでデータをフェッチする方法

まずは、useEffectを使ったデータ取得方法を見てみましょう。今回のサンプルコードでは、JSONPlaceholderからユーザーデータを取得し、一覧を表示します。

```TypeScript:
import axios from "axios";
import { useEffect, useState } from "react";

interface User {
  id: string;
  username: string;
  email: string;
  address: {
    city: string;
  };
}

const api = "https://jsonplaceholder.typicode.com/users";

function App() {
  const [users, setUsers] = useState<User[]>([]);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    setIsLoading(true);
    setError(null);
    const getUser = async () => {
      try {
        const response = await axios.get(api);
        setUsers(response.data);
      } catch (err) {
        setError("Failed to fetch users.");
        console.error(err);
      } finally {
        setIsLoading(false);
      }
    };
    getUser();
  }, []);
  if (error) return <div>{error}</div>;
  if (isLoading) return <div>Loading...</div>;
  return (
    <div className="App">
      <ul>
        {users.map((user) => (
          <li key={user.id}>
            <p>{user.username}</p>
            <p>{user.email}</p>
            <p>{user.address.city}</p>
          </li>
        ))}
      </ul>
    </div>
  );
}

export default App
```
このコードでは、useEffect内でAPIを叩いています。useEffectの第二引数を空配列にしているので、初回マウント時のみ発火します。

コードを見ると、ユーザー情報、ローディング状態、エラー状態を管理するためにuseStateが多く使われています。また、useEffectにはキャッシュのメカニズムがないため、データに変更がない場合でもリロードするたびにデータ取得を行っています。

これらの問題を解決してくれるのが、useSWRです！

## useSWRでデータをフェッチする方法

次に、useSWRを使ったデータ取得方法を見てみましょう。

useSWRのデータの取得方法は以下のようになります。（公式ドキュメント参照）

```TypeScript
import useSWR from 'swr'
import axios from 'axios'

// fetchを使用した場合
const fetcher = url => fetch(url).then(r => r.json()) // urlはuseSWR()の第一引数で渡されたURL

// axiosを使用した場合
const fetcher = url => axios.get(url).then(res => res.data)

function Profile() {
	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）で、フェッチ関数に渡されます。フェッチ関数はデータを返す非同期関数で、ネイティブのfetchやAxiosのようなツールを使用できます。

このフックは、リクエストの状態にもとづいて data と isLoading, error の三つの値を返します。

useSWRを使用する利点は以下の通りです。

- キャッシュにより、同じデータを何度も取得する必要がなく、アプリケーションのレスポンス性能が向上。
- デフォルトでデータを自動的に再取得する仕組みを持っているため、データが変更された際に自動的に最新の情報を反映可能。
- 少ないコードで効果的にデータを取得できる。

では、実際にuseSWRを使用してユーザー情報を取得してみましょう！

```TypeScript:App.tsx
import { useFetchUser } from './hooks/useFetchUser'

function App() {
  const { data, error, loading } = useFetchUser();
  if (error) {
    console.error(error);
    return (
      <div>Failed to fetch users.</div>
    )
  };
  if (loading) return <div>Loading...</div>
  return (
    <div>
      <ul>
        {data?.map((user) => (
          <li key={user.id}>
            <p>{user.username}</p>
            <p>{user.email}</p>
            <p>{user.address.city}</p>
          </li>
        ))}
      </ul>
    </div>
  )
}

export default App
```

```TypeScript:useFetchUser.ts
import axios from "axios";
import useSWR from "swr";
import { User } from "../type/type";

const fetcher = (url :string) => axios.get(url).then((res) => res.data);
const api = "https://jsonplaceholder.typicode.com/users"

export const useFetchUser = () => {
  const { data, error, isLoading } = useSWR<User[]>(api, fetcher);
  return {
    data,
    error,
    loading: isLoading,
  }
}
```

```TypeScript.ts
export interface User {
  id: number;
  username: string;
  email: string;
  address: {
    city: string;
  };
}
```

useFetchUserというフックを作成し、そこでAPIを叩いています。取得したdata、error、loadingを返却します。

App.tsxでuseFetchUserからdata、error、loadingを受け取り、エラー時、ローディング時、成功時の処理をしています。

...めっちゃ簡単！記述が凄いシンプルになりました！ステートでの管理がなくなったのでコードも見やすくなりました。

## まとめ

今まで「APIを叩く→useEffectを使う!!!」という考えでしたが、これを機にuseSWRを積極的に使ってみようと思います。

間違ってる点などございましたら、ぜひコメントで教えてください。

## 参考

### 公式ドキュメント
- [useSWR公式ドキュメント](https://swr.vercel.app/ja)
### ブログ
- [【React】Suspense・SWRは何を解決するのか](https://tech-blog.rakus.co.jp/entry/20230908/suspense_swr#SWR%E3%81%A8%E3%81%AF)
### YouTube
- [React初心者が間違うuseEffectの使い方【useSWRを利用】](https://www.youtube.com/watch?v=Kb8YbE8NxZ0)