はじめに
インストール
Reactプロジェクトディレクトリ内で、以下のコマンドを実行します。
pnpm add swr
クイックスタート
JSONデータを使用する通常のRESTful APIの場合、最初にネイティブのfetch
のラッパーであるfetcher
関数を生成する必要があります。
const fetcher = (...args) => fetch(...args).then(res => res.json())
GraphQL APIやAxiosのようなライブラリを使用する場合は、独自のfetcher関数を生成できます。詳細な例についてはこちらを参照してください。
次に、useSWR
をインポートし、任意の関数コンポーネント内で使用を開始できます。
import useSWR from 'swr'
function Profile () {
const { data, error, isLoading } = useSWR('/api/user/123', fetcher)
if (error) return <div>failed to load</div>
if (isLoading) return <div>loading...</div>
// render data
return <div>hello {data.name}!</div>
}
通常、リクエストには「読み込み中」、「準備完了」、「エラー」の3つの状態があります。data
、error
、isLoading
の値を使用してリクエストの現在の状態を判断し、対応するUIを返します。
再利用可能なコンポーネントの作成
ウェブアプリを構築する際には、UIの多くの場所でデータを再利用する必要がある場合があります。SWRの上に再利用可能なデータフックを作成することは非常に簡単です。
function useUser (id) {
const { data, error, isLoading } = useSWR(`/api/user/${id}`, fetcher)
return {
user: data,
isLoading,
isError: error
}
}
そして、それをコンポーネントで使用します。
function Avatar ({ id }) {
const { user, isLoading, isError } = useUser(id)
if (isLoading) return <Spinner />
if (isError) return <Error />
return <img src={user.avatar} />
}
このパターンを採用することで、**取得**データを命令型で処理する必要がなくなります。リクエストを開始し、読み込み状態を更新し、最終結果を返す必要はありません。代わりに、コードはより宣言的になります。コンポーネントで使用されるデータを指定するだけで済みます。
例
現実世界の例として、当社のウェブサイトではナビゲーションバーとコンテンツが表示されますが、どちらもuser
に依存しています。
従来は、最上位レベルのコンポーネントでuseEffect
を使用して一度データを取得し、プロップスを介して子コンポーネントに渡していました(現時点ではエラー状態は処理しません)。
// page component
function Page () {
const [user, setUser] = useState(null)
// fetch data
useEffect(() => {
fetch('/api/user')
.then(res => res.json())
.then(data => setUser(data))
}, [])
// global loading state
if (!user) return <Spinner/>
return <div>
<Navbar user={user} />
<Content user={user} />
</div>
}
// child components
function Navbar ({ user }) {
return <div>
...
<Avatar user={user} />
</div>
}
function Content ({ user }) {
return <h1>Welcome back, {user.name}</h1>
}
function Avatar ({ user }) {
return <img src={user.avatar} alt={user.name} />
}
通常、すべてのデータ取得を最上位レベルのコンポーネントに保持し、ツリーの深くに位置するすべてのコンポーネントにプロップスを追加する必要があります。ページにデータの依存関係がさらに追加されると、コードのメンテナンスが困難になります。
コンテキスト(新しいタブで開きます)を使用してプロップスの受け渡しを回避できますが、動的なコンテンツの問題が残ります。ページコンテンツ内のコンポーネントは動的になる可能性があり、最上位レベルのコンポーネントはその子コンポーネントでどのようなデータが必要になるかを認識できない可能性があります。
SWRはこの問題を完璧に解決します。作成したばかりのuseUser
フックを使用すると、コードを次のようにリファクタリングできます。
// page component
function Page () {
return <div>
<Navbar />
<Content />
</div>
}
// child components
function Navbar () {
return <div>
...
<Avatar />
</div>
}
function Content () {
const { user, isLoading } = useUser()
if (isLoading) return <Spinner />
return <h1>Welcome back, {user.name}</h1>
}
function Avatar () {
const { user, isLoading } = useUser()
if (isLoading) return <Spinner />
return <img src={user.avatar} alt={user.name} />
}
データは、データが必要なコンポーネントに**バインド**され、すべてのコンポーネントは互いに**独立**しています。すべての親コンポーネントは、データやデータの受け渡しについて何も知る必要はありません。単にレンダリングするだけです。コードははるかにシンプルになり、メンテナンスが容易になります。
最も素晴らしいのは、同じSWRキーを使用するため、APIへのリクエストは**1回**だけで済み、リクエストは自動的に**重複排除**、**キャッシュ**、**共有**されることです。
また、アプリケーションはユーザーのフォーカスやネットワークの再接続時にデータを再取得する機能も備えています!つまり、ユーザーのラップトップがスリープ状態から復帰したり、ブラウザのタブを切り替えた場合、データは自動的に更新されます。