SWRの紹介:リモートデータフェッチ用のReactフック
公開: 2022-03-10SWRは、Vercel(以前のZEIT)によって作成された軽量ライブラリであり、React Hooksを使用してリアルタイムでデータをフェッチ、キャッシュ、または再フェッチできます。 React Suspenseを使用して構築されているため、コンポーネントは、データを含め、レンダリングする前に何かを「待機」できます。 SWRには、依存フェッチ、再検証へのフォーカス、スクロール位置の回復などの優れた機能も付属しています。 また、バックエンドに依存せず、TypeScriptを適切にサポートしているため、非常に強力なツールです。 明るい未来のパッケージです。
なぜあなたは気にする必要がありますか? APIからデータをフェッチするだけでなく、キャッシュや依存フェッチなどを実行できるライブラリを探している場合は、注意が必要です。 このチュートリアルで説明する内容は、多くの可動部品を使用してReactアプリケーションを構築するときに役立ちます。 AxiosとFetchAPIを使用する必要がありますが、SWRとの違いを比較しますが、それらの実装方法については詳しく説明しません。
このガイドでは、Pokemon APIからデータを要求するPokedexアプリを構築することにより、リモートデータフェッチ用のReactフックを紹介します。 また、SWRに付属する他の機能についても詳しく説明し、Fetch APIやAxiosライブラリなどの一般的なソリューションとの違いを強調し、このライブラリを使用する理由とSWRに注意を払う必要がある理由を説明します。
それでは、基本的な質問に答えることから始めましょう:SWRとは何ですか?
SWRとは何ですか?
SWRは、stale-while-revalidateの初期化です。 これは、リモートデータフェッチ用のReactHooksライブラリです。 SWRは、3つの主要なステップで機能します。最初に、キャッシュからデータを返し(古い部分)、次にフェッチ要求を送信し(再検証部分)、最後に最新のデータを受け取ります。 しかし、心配する必要はありません。SWRがこれらすべての手順を処理してくれます。 私たちがしなければならない唯一のことは、 useSWR
フックにリクエストを行うために必要なパラメータを与えることです。
SWRには、次のような優れた機能もあります。
- バックエンドにとらわれない
- 高速ページナビゲーション
- 焦点の再検証
- インターバルポーリング
- 重複排除をリクエストする
- 局所突然変異
- ページ付け
- TypeScript対応
- SSRサポート
- サスペンスモード
- ReactNativeのサポート
- 軽量。
魔法のように聞こえますか? そうですね、SWRは物事を簡素化し、Reactアプリのユーザーエクスペリエンスを確実に向上させます。 プロジェクトに実装を開始すると、このフックが便利な理由がわかります。
パッケージの名前はswr
またはSWRであり、SWR機能を取得するために使用されるフックの名前はuseSWR
であることを知っておくことが重要です。
理論的には、SWRはデータフェッチを強化するために必要なものかもしれません。 ただし、アプリでHTTPリクエストを行うには、FetchAPIとAxiosライブラリの2つの優れた方法がすでにあります。
では、なぜ新しいライブラリを使用してデータを取得するのでしょうか。 次のセクションで、この正当な質問に答えてみましょう。
FetchおよびAxiosとの比較
React AppsでHTTPリクエストを作成する方法はすでにたくさんありますが、最も人気のある2つはFetchAPIとAxiosライブラリです。 どちらも優れており、データを簡単にフェッチまたは送信できます。 ただし、操作が完了すると、データのキャッシュやページ分割には役立ちません。自分で行う必要があります。
AxiosまたはFetchは要求を処理し、期待される応答を返すだけで、それ以上のことはありません。
また、SWRと比較すると、内部のSWRはFetch APIを使用してサーバーにデータを要求するため、少し異なります。これは、サーバーの上に構築された一種のレイヤーです。 ただし、キャッシング、ページ付け、スクロール位置の回復、依存フェッチなどの優れた機能があり、正確には、AxiosやFetchにはない一定レベルの反応性があります。 このような機能があると、React Appsが高速でユーザーフレンドリーになり、コードのサイズが大幅に削減されるため、これは大きな利点です。
結論として、HTTPリクエストの処理に役立つ場合でも、SWRはAxiosまたはFetchと同じではないことに注意してください。 SWRはそれらよりも高度であり、アプリをバックエンドと同期させ続けるためのいくつかの拡張機能を提供し、アプリのパフォーマンスを向上させます。
SWRがAxiosライブラリまたはFetchAPIと比較した違いがわかったので、そのようなツールを使用する理由を詳しく見ていきましょう。
推奨読書: FetchとAxiosに反応してRESTAPIを消費する
データフェッチにSWRを使用する理由
前に述べたように、SWRには、アプリの使いやすさを簡単に向上させるのに役立ついくつかの便利な機能が付属しています。 SWRを使用すると、 useSWRPages
を使用してデータをすぐにページ分割したり、別のリクエストに依存するデータをフェッチしたり、特定のページに戻ったときにスクロール位置を回復したりすることができます。
通常、サーバーからデータをフェッチしている間、ユーザーに読み込みメッセージまたはスピナーを表示します。 また、SWRを使用すると、APIから新しいデータを取得するときに、キャッシュされたデータまたは古いデータをユーザーに表示することで、データを改善できます。 そして、その操作が完了すると、新しいバージョンを表示するためにデータが再検証されます。 また、何もする必要はありません。SWRは、最初にデータをフェッチしたときにデータをキャッシュし、新しいリクエストが行われたときに自動的にデータを取得します。
これまでのところ、AxiosまたはFetchでSWRを使用する方が、構築しようとしているものに明らかに依存する理由はすでにわかっています。 ただし、多くの場合、SWRを使用することをお勧めします。これは、データをフェッチして返すだけではない優れた機能を備えているためです。
そうは言っても、Reactアプリの構築を開始し、SWRライブラリを使用してリモートデータをフェッチできるようになりました。
それでは、新しいプロジェクトを設定することから始めましょう。
セットアップ
冒頭で述べたように、ポケモンAPIからデータをフェッチするアプリを作成します。 必要に応じて別のAPIを使用することもできますが、今はそれを使い続けます。
また、新しいアプリを作成するには、ターミナルで次のコマンドを実行する必要があります。
npx create-react-app react-swr
次に、最初にReactアプリを保持するフォルダーに移動して、SWRライブラリーをインストールする必要があります。
cd react-swr
そして、ターミナルで次のコマンドを実行して、SWRパッケージをインストールします。
yarn add swr
または、npmを使用している場合:
npm install swr
これですべてのセットアップが完了しました。SWRの使用を開始するために、プロジェクトを次のように構成しましょう。
src ├── components | └── Pokemon.js ├── App.js ├── App.test.js ├── index.js ├── serviceWorker.js ├── setupTests.js ├── package.json ├── README.md ├── yarn-error.log └── yarn.lock
ご覧のとおり、フォルダ構造は単純です。 注意すべき唯一のことは、 Pokemon.js
ファイルを保持するcomponents
フォルダーです。 APIからデータを取得すると、後で1つのポケモンを表示するためのプレゼンテーションコンポーネントとして使用されます。
すごい! これで、 useSWR
を使用してAPIからデータのフェッチを開始できます。
リモートデータの取得
SWRパッケージには、上記で見たようにいくつかの便利な機能があります。 ただし、このライブラリを構成するには、ローカルまたはグローバルの2つの方法があります。
ローカルセットアップとは、新しいファイルを作成するたびに、リモートデータをフェッチできるようにSWRを再度セットアップする必要があることを意味します。 また、グローバルセットアップでは、 fetcher
関数を一度宣言してどこでも使用できるため、構成の一部をさまざまなファイル内で再利用できます。
心配はいりません。この記事では両方を確認しますが、とりあえず、手を汚して、 App.js
ファイルに意味のあるコードを追加しましょう。
データの表示
import React from 'react' import useSWR from 'swr' import { Pokemon } from './components/Pokemon' const url = 'https://pokeapi.co/api/v2/pokemon' const fetcher = (...args) => fetch(...args).then((res) => res.json()) function App() { const { data: result, error } = useSWR(url, fetcher) if (error) return <h1>Something went wrong!</h1> if (!result) return <h1>Loading...</h1> return ( <main className='App'> <h1>Pokedex</h1> <div> {result.results.map((pokemon) => ( <Pokemon key={pokemon.name} pokemon={pokemon} /> ))} </div> </main> ) } export default App
ご覧のとおり、まずSWRライブラリからuseSWR
をインポートします。 これにより、データを取得するAPIのURLと、これらのデータをフェッチする関数が宣言されます。
ここでは、関数fetcher
を使用してデータをJSONに変換します。 フェッチされたデータを引数として受け取り、何かを返します。
ここでは、パラメーターとして受け取ったデータのタイプと長さがわからないため、Rest演算子( (...args)
)を使用していることに注意してください。したがって、 fetch
への引数として再度渡す前に、すべてをコピーします。データをJSONに変換してuseSWR
によって提供されるメソッド。
そうは言っても、APIのfetcher
とurl
をパラメーターとしてuseSWR
フックに渡すことができるようになりました。 これで、リクエストを実行できるようになり、フェッチされたデータとエラー状態の2つの状態が返されます。 また、 data: result
はdata.result
と同じであり、オブジェクトの破棄を使用してdata
からresult
を取得します。
戻り値を使用して、データが正常にフェッチされたかどうかを確認し、ループすることができます。 そして、ユーザーごとに、ポケモンコンポーネントを使用して表示します。
これでデータが得られ、それをPokemonコンポーネントに渡します。次に、 Pokemon.js
を更新して、データを受信して表示できるようにします。
ポケモンコンポーネントの作成
import React from 'react' import useSWR from 'swr' const fetcher = (...args) => fetch(...args).then((res) => res.json()) export const Pokemon = ({ pokemon }) => { const { name } = pokemon const url = 'https://pokeapi.co/api/v2/pokemon/' + name const { data, error } = useSWR(url, fetcher) if (error) return <h1>Something went wrong!</h1> if (!data) return <h1>Loading...</h1> return ( <div className='Card'> <span className='Card--id'>#{data.id}</span> <img className='Card--image' src={data.sprites.front_default} alt={name} /> <h1 className='Card--name'>{name}</h1> <span className='Card--details'> {data.types.map((poke) => poke.type.name).join(', ')} </span> </div> ) }
ここには、APIから単一のポケモンデータを受け取り、それを表示するコンポーネントがあります。 ただし、受信したデータには必要なすべてのフィールドが含まれているわけではないため、完全なポケモンオブジェクトを取得するには、APIに別のリクエストを行う必要があります。
ご覧のとおり、今回はポケモンの名前をURLに追加しても、同じプロセスを使用してデータを取得します。
ちなみに、破壊に慣れていない場合、 ({ pokemon })
は、小道具を受け取り、 props.pokemon
を使用してポケモンオブジェクトにアクセスするのと同じです。 オブジェクトまたは配列から値を引き出すための省略形です。
それが整った状態で、プロジェクトのルートフォルダーに移動し、ターミナルで次のコマンドを実行すると、次のようになります。
yarn start
または、npmを使用している場合:
npm start
データがPokemonAPIから正常にフェッチされ、期待どおりに表示されることがわかります。
すごい! これで、SWRを使用してリモートデータをフェッチできるようになりました。 ただし、この設定はローカル設定であり、 App.js
とPokemon.js
が同じフェッチャー関数を使用して同じことを行うことがすでにわかるため、少し冗長になる可能性があります。
しかし幸いなことに、このパッケージには、SWRをグローバルに構成するのに役立つSWRConfig
という名前の便利なプロバイダーが付属しています。 これは、子コンポーネントがグローバル構成を使用できるようにするラッパーコンポーネントであるため、フェッチャー関数を使用できます。
SWRをグローバルにセットアップするには、AppコンポーネントがReact DOMを使用してレンダリングされる場所であるため、 index.js
ファイルを更新する必要があります。 必要に応じて、 App.js
ファイルでSWRConfig
を直接使用できます。
SWRをグローバルに構成する
import React from 'react' import ReactDOM from 'react-dom' import { SWRConfig } from 'swr' import App from './App' import './index.css' const fetcher = (...args) => fetch(...args).then((res) => res.json()) ReactDOM.render( <React.StrictMode> <SWRConfig value={{ fetcher }}> <App /> </SWRConfig> </React.StrictMode>, document.getElementById('root') )
ご覧のとおり、まず、上位コンポーネントをラップする必要があるプロバイダーであるSWRConfig
、またはSWR機能を使用する必要があるReactアプリの一部をインポートします。 configのオブジェクトを期待する値を小道具として取ります。 configオブジェクトに複数のプロパティを渡すことができます。ここでは、データをフェッチする関数が必要です。
ここで、すべてのファイルでfetcher
関数を宣言する代わりに、ここでそれを作成し、値としてSWRConfig
に渡します。 これにより、別の関数を作成せずにアプリの任意のレベルでデータを取得できるようになり、冗長性を回避できます。
その上、 fetcher
はfetcherと同じfetcher: fetcher
、それはES6によって提案された単なる構文糖衣です。 この変更に伴い、グローバル構成を使用するようにコンポーネントを更新する必要があります。
グローバルSWR構成の使用
import React from 'react' import useSWR from 'swr' import { Pokemon } from './components/Pokemon' const url = 'https://pokeapi.co/api/v2/pokemon' function App() { const { data: result, error } = useSWR(url) if (error) return <h1>Something went wrong!</h1> if (!result) return <h1>Loading...</h1> return ( <main className='App'> <h1>Pokedex</h1> <div> {result.results.map((pokemon) => ( <Pokemon key={pokemon.name} pokemon={pokemon} /> ))} </div> </main> ) } export default App
これで、 url
とfetcher
メソッドを渡す代わりに、 useSWR
にurl
を渡すだけで済みます。 また、ポケモンコンポーネントを少し調整してみましょう。
import React from 'react' import useSWR from 'swr' export const Pokemon = ({ pokemon }) => { const { name } = pokemon const url = 'https://pokeapi.co/api/v2/pokemon/' + name const { data, error } = useSWR(url) if (error) return <h1>Something went wrong!</h1> if (!data) return <h1>Loading...</h1> return ( <div className='Card'> <span className='Card--id'>#{data.id}</span> <img className='Card--image' src={data.sprites.front_default} alt={name} /> <h1 className='Card--name'>{name}</h1> <span className='Card--details'> {data.types.map((poke) => poke.type.name).join(', ')} </span> </div> ) }
内部でuseSWR
に関数を渡すグローバル構成のおかげで、フェッチャー関数がもうないことがすでにわかります。
これで、アプリのどこでもグローバルフェッチャー関数を使用できます。 useSWR
フックがリモートデータをフェッチするために必要なのはURLだけです。
ただし、カスタムフックを作成して、URLを何度も宣言しないようにすることで、セットアップをさらに強化できます。代わりに、パラメーターとしてパスを渡すだけです。
カスタムフックを作成することによる高度なセットアップ
これを行うには、プロジェクトのルートにuseRequest.js
という名前の新しいファイルを作成し(任意の名前を付けることができます)、その下にこのコードブロックを追加する必要があります。
import useSwr from 'swr' const baseUrl = 'https://pokeapi.co/api/v2' export const useRequest = (path, name) => { if (!path) { throw new Error('Path is required') } const url = name ? baseUrl + path + '/' + name : baseUrl + path const { data, error } = useSwr(url) return { data, error } }
ここでは、パスとオプションで名前を受け取り、それをベースURLに追加して、完全なURLを作成する関数があります。 次に、nameパラメータが受信されたかどうかをチェックし、結果として処理します。
次に、そのURLがパラメーターとしてuseSWR
フックに渡され、リモートデータをフェッチして返すことができるようになります。 また、パスが渡されない場合は、エラーがスローされます。
すごい! ここで、カスタムフックを使用するために、コンポーネントを少し調整する必要があります。
import React from 'react' import { useRequest } from './useRequest' import './styles.css' import { Pokemon } from './components/Pokemon' function App() { const { data: result, error } = useRequest('/pokemon') if (error) return <h1>Something went wrong!</h1> if (!result) return <h1>Loading...</h1> return ( <main className='App'> <h1>Pokedex</h1> <div> {result.results.map((pokemon) => ( <Pokemon key={pokemon.name} pokemon={pokemon} /> ))} </div> </main> ) } export default App
ここで、SWRフックを使用する代わりに、その上に構築されたカスタムフックを使用して、期待どおりにパスを引数として渡します。 これにより、すべてが以前と同じように機能しますが、構成ははるかにクリーンで柔軟になります。
ポケモンコンポーネントも更新しましょう。
import React from 'react' import { useRequest } from '../useRequest' export const Pokemon = ({ pokemon }) => { const { name } = pokemon const { data, error } = useRequest('/pokemon', name) if (error) return <h1>Something went wrong!</h1> if (!data) return <h1>Loading...</h1> return ( <div className='Card'> <span className='Card--id'>#{data.id}</span> <img className='Card--image' src={data.sprites.front_default} alt={name} /> <h1 className='Card--name'>{name}</h1> <span className='Card--details'> {data.types.map((poke) => poke.type.name).join(', ')} </span> </div> ) }
私たちのカスタムフックがどのように物事をより簡単でより柔軟にするかをすでに見ることができます。 ここでは、 useRequest
にフェッチするポケモンの名前を追加で渡す必要があり、それがすべてを処理します。
このクールなライブラリを楽しんでいただければ幸いです。ただし、SWRには非常に多くの機能があり、そのうちの1つはデータを簡単にページ付けするためのフックであるuseSWRPages
です。 それでは、プロジェクトでそのフックを使用しましょう。
useSWRPages
を使用してデータをページ付けする
SWRを使用すると、データを簡単にページ付けしてその一部のみを要求し、必要に応じてデータを再フェッチして次のページに表示できます。
それでは、プロジェクトusePagination.js
のルートに新しいファイルを作成し、それをページネーションのカスタムフックとして使用しましょう。
import React from 'react' import useSWR, { useSWRPages } from 'swr' import { Pokemon } from './components/Pokemon' export const usePagination = (path) => { const { pages, isLoadingMore, loadMore, isReachingEnd } = useSWRPages( 'pokemon-page', ({ offset, withSWR }) => { const url = offset || `https://pokeapi.co/api/v2${path}` const { data: result, error } = withSWR(useSWR(url)) if (error) return <h1>Something went wrong!</h1> if (!result) return <h1>Loading...</h1> return result.results.map((pokemon) => ( <Pokemon key={pokemon.name} pokemon={pokemon} /> )) }, (SWR) => SWR.data.next, [] ) return { pages, isLoadingMore, loadMore, isReachingEnd } }
ご覧のとおり、ここでは、データのページ分割を簡単に行えるヘルパーであるuseSWRPages
をインポートすることから始めます。 キャッシュにも使用されるリクエストpokemon-page
のキー、データが正常に取得された場合にコンポーネントを返すデータをフェッチする関数、 SWR
オブジェクトを取得してデータをリクエストする別の関数の4つの引数を受け取ります。次のページ、および依存関係の配列。
データがフェッチされると、関数useSWRPages
はいくつかの値を返しますが、ここでは4つの値が必要です。データとともに返されるコンポーネントであるpages
、データが現在フェッチされているかどうかをチェックする関数isLoadingMore
、フェッチを支援する関数loadMore
です。より多くのデータ、および取得するデータがまだあるかどうかを判断するメソッドisReachingEnd
。
これで、データをページ分割するために必要な値を返すカスタムフックができました。これで、 App.js
ファイルに移動して、少し調整できます。
import React from 'react' import { usePagination } from './usePagination' import './styles.css' export default function App() { const { pages, isLoadingMore, loadMore, isReachingEnd } = usePagination( '/pokemon' ) return ( <main className='App'> <h1>Pokedex</h1> <div>{pages}</div> <button onClick={loadMore} disabled={isLoadingMore || isReachingEnd} > Load more... </button> </main> ) }
usePagination
フックがインポートされると、パスをパラメーターとして渡し、戻り値を取得できるようになります。 また、 pages
はコンポーネントであるため、データなどをループする必要はありません。
次に、ボタンの関数loadMore
を使用してさらにデータをフェッチし、取得操作が終了していない場合、またはフェッチするデータがない場合は無効にします。
すごい! この変更により、プロジェクトのルートを参照し、このコマンドでサーバーを起動してアプリをプレビューできるようになりました。
yarn start
または、npmを使用している場合:
npm start
データが正常にフェッチされていることを確認する必要があります。ボタンをクリックすると、SWRによって新しいデータが取得されます。
これまで、SWRライブラリを実際に見てきましたが、その価値を見つけていただければ幸いです。 ただし、まだいくつかの機能があります。 次のセクションでは、これらの機能について詳しく見ていきましょう。
SWRのその他の機能
SWRライブラリには、Reactアプリの構築方法を簡素化する便利な機能がたくさんあります。
フォーカスの再検証
これは、ページのフォーカスを変更したり、タブを切り替えたりするときに、データを正確に更新または再検証できる機能です。 デフォルトでは、この機能は有効になっていますが、ニーズに合わない場合は、とにかく無効にすることができます。 これは、高レベルの頻度で更新されるデータがある場合に特に役立ちます。
間隔で再フェッチ
SWRライブラリを使用すると、一定時間後にデータを再フェッチできます。 データが高速で変更される場合や、データベースから新しい情報を取得するために新しいリクエストを行う必要がある場合に便利です。
ローカルミューテーション
SWRを使用すると、新しいデータがフェッチされたときに自動的に更新される一時的なローカル状態を設定できます(再検証)。 この機能は、特にオフラインファーストのアプローチを扱う場合に役立ち、データを簡単に更新するのに役立ちます。
スクロール位置の回復
この機能は、特に巨大なリストを処理する場合に非常に便利です。 ページに戻った後、スクロール位置を回復することができます。 いずれにせよ、それはあなたのアプリの使いやすさを向上させます。
依存フェッチ
SWRを使用すると、他のデータに依存するデータをフェッチできます。 つまり、データAをフェッチでき、その操作が完了すると、ウォーターフォールを回避しながらデータBをフェッチするためにそれを使用します。 また、この機能は、リレーショナルデータがある場合に役立ちます。
とはいえ、SWRはあらゆる問題でユーザーエクスペリエンスを向上させるのに役立ちます。 それよりも多くの機能があり、多くの場合、FetchAPIまたはAxiosライブラリよりも使用することをお勧めします。
結論
この記事全体を通して、SWRが素晴らしいライブラリである理由を見てきました。 React Hooksを使用したリモートデータフェッチを可能にし、ページネーション、データのキャッシュ、間隔での再フェッチ、スクロール位置の回復など、すぐに使用できるいくつかの高度な機能を簡素化するのに役立ちます。 SWRはバックエンドに依存しないため、あらゆる種類のAPIまたはデータベースからデータをフェッチできます。 決定的に、SWRはReactアプリのユーザーエクスペリエンスを大幅に向上させます。SWRには明るい未来があり、それを監視するか、次のReactアプリでより適切に使用する必要があります。
完成したプロジェクトをここでライブプレビューできます。
読んでくれてありがとう!
次のステップ
次のリンクを確認して、このチュートリアルの範囲を超えて理解を深めることができます。
- SWR
- SWRドキュメント
SmashingMagの詳細:
- Reactのスタイリングコンポーネント
- イマーでより良いレデューサー
- Reactの高次コンポーネント
- Tailwindを使用した再利用可能なReactコンポーネントの構築