🐥

【React】クライアントでデータを素早くフェッチするSWRの使い方

2020/12/15に公開

この記事ではクライアントデータをフェッチする際に利用できるライブラリーSWRの使い方について解説していきます。

SWRとは?

SWRはデータフェッチに使うReactHookライブラリの一つで、stale-while-revalidateの名前が由来になっています。

stale-while-revalidateとは、最初にキャッシュを表示してサーバーにリクエストを投げてキャッシュを更新するキャッシュ管理方法です。
更新したキャッシュは次回に同じリクエストが投げられた時に使用されます。

  1. キャッシュを表示
  2. サーバーにリクエスト
  3. 取得データでキャッシュを更新・データの表示

このstale-while-revalidateにより、データを継続的に更新し、データをより早く表示でしています。

SWRのインストール

まずはインストールが必要です。対象のプロジェクトに以下のどちらかのコマンドを叩きましょう。
yarn add swr
npm install swr

SWRを使う

それでは実際にSWRを使ってみます。以下のソースではデータをフェッチできたときにそのままデータを表示しています。

import useSWR from 'swr'

//fetcher関数の作成
const fetcher = url => fetch(url).then(r => r.json())

export default function index () {
  const { data, error } = useSWR('/api/user', fetcher)
  //エラー
  if (error) return <div>failed to load</div>
  //ロード中
  if (!data) return <div>loading...</div>
  //成功
  return <pre>{JSON.stringify(data, null, 2)}</pre>
}

useSWRフックを使ってデータを取得します。

  1. データを一意に識別するkey
  2. データを返す非同期関数のfetcher

この2つの引数が必要です。keyはURLの指定、fetcherはfetch関数やAxiosが指定されます。今回はfetch関数で外部のデータを取得して、JSON形式に変換しているだけです。

そして、useSWRフックの戻り値はdataとerrorの2つが返却。この2つのデータにより、リクエストが3つの状態を持ちます

  • エラー:errorに値がある場合
  • ロード中:errorに値がなく、データに値がない場合
  • 取得成功:データに値がある場合

キャッシュを利用して簡潔に書く

SWRを使って良い点はキャッシュを利用してリクエスト回数を減らしながらコードを簡潔に書けることです。

従来のデータ取得

たとえば、従来の方法でデータをフェッチすると親コンポーネントからそれぞれのコンポーネントに取得したデータを引き渡す必要がありました。

以下のソースでは親コンポーネントが初期表示された際にuseEffectでデータを取得してAvatarとContentにユーザー情報を渡しています。

//ページ表示
export default function Index () {
  //ユーザー情報
  const [user, setUser] = useState(null)

  //初期表示時にデータフェッチ
  useEffect(() => {
    fetch('/api/user')
      .then(res => res.json())
      .then(data => setUser(data))
  }, [])

  //ロード中
  if (!user) return <p>Loading ...</p>

  //成功
  return <div>
    <Avatar user={user} />
    <Content user={user} />
  </div>
}

//コンテンツ
function Content ({ user }) {
  return <h1>ようこそ {user.name}さん</h1>
}

//アバター
function Avatar ({ user }) {
  const avatar_style = {
    borderRadius : "50%",
    width        : "50px",
  }

  return <img 
            src={user.avatar}
            alt={user.name}
            style={avatar_style}/>
}

SWRを使ってデータ取得する方法

しかし、SWRを使ってフックを作れば引数を渡す処理が無くなり、親コンポーネントと子コンポーネントの依存関係を減らすことが可能です。

import useSWR from 'swr'

//フェッチ関数(JSON形式に変換)
const fetcher = url => fetch(url).then(r => r.json())

//ユーザー情報の取得
function useUser () {
  const { data, error } = useSWR(`/api/user`, fetcher)
  return {
    user: data,
    isLoading: !error && !data,
    isError: error
  }
}

//ページ表示
export default function Index () {

  return (
    <div>
      <Avatar />
      <Content/>
    </div>
  )
}

//コンテンツ
function Content () {
  const { user, isLoading } = useUser();    //ユーザー情報の取得
  if (isLoading) return <p>Loading ...</p>  //ロード中
  return <h1>ようこそ {user.name}さん</h1>   
}

//アバター
function Avatar () {
  const avatar_style = {
    borderRadius : "50%",
    width        : "50px",
  };

  
  const { user, isLoading } = useUser()     //ユーザー情報の取得
  if (isLoading) return <p>Loading ...</p>  //ロード中

  return <img 
            src={user.avatar}
            alt={user.name}
            style={avatar_style}/>
}

それぞれのデータが必要になった場面でuseUserフックを利用することで親コンポーネントは不必要なデータ(user)を保持しておくことが不要になります。
これにより、それぞれが独立したコンポーネントになり、保守性の向上につながるのです。

リクエスト回数が1回で済む

しかし、このソースを見て多くの人が懸念する点がリクエスト回数でしょう。従来の方法ではuseEffectで1回の呼び出しでデータを取得していますが、SWRでは2回も呼び出しています。

実は、この場合でもSWRを使えば1回のリクエストで済むようになっています。理由はuseSWRに同じキー(URL)を指定しているため自動的にキャッシュされるためです。

このようにSWRを使えば不要なリクエストを減らし、簡潔にコードを書くことができるのです。

参考

https://swr.vercel.app/

https://engineering.mercari.com/blog/entry/2019-12-17-130000/

https://web.dev/stale-while-revalidate/

Discussion