この記事では TanStack(TanStack は以前の React Query)の Query の使い方と基本の動きを紹介しています。
色々な機能がありますが、この記事ではまずは基本的な使い方のメモ。
利用するコードベース
以下で作ったものをベースにしています。
主なライブラリのバージョンは以下。
% npm list --depth=0
├── @tanstack/react-query@4.20.4
├── @types/react-dom@18.0.9
├── @types/react@18.0.26
├── @typescript-eslint/eslint-plugin@5.46.0
├── @typescript-eslint/parser@5.46.0
├── @vitejs/plugin-react@3.0.0
├── eslint-config-prettier@8.5.0
├── eslint-plugin-react-hooks@4.6.0
├── eslint-plugin-react@7.31.11
├── eslint@8.29.0
├── prettier-plugin-organize-imports@3.2.1
├── prettier@2.8.1
├── react-dom@18.2.0
├── react@18.2.0
├── typescript@4.9.4
├── vite-tsconfig-paths@4.0.2
└── vite@4.0.0
インストール
以下の実行でOKです。
yarn add @tanstack/react-query
クエリの実行 – useQuery
基本
基本の使い方はハイライト部分の通り。非常にシンプルにサーバ処理の管理(通信中、エラー、取得後の描画)を行うことができます。
isLoading, isError などは vue-apollo などでもおなじみですね。この類のライブラリを使ったことあれば他のライブラリを利用するのもそんな困らない気がします。
ここで面白いと思ったのは、useQuery が提供する機能と、実際の API をコールする処理が別という点です。ライブラリや特定の API 方式に依存しない形にできる点は TanStack ぽさを感じました。
import {
QueryClient,
QueryClientProvider,
useQuery,
} from '@tanstack/react-query'
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
import { useState } from 'react'
import './App.css'
// 1. client を生成
const queryClient = new QueryClient()
// 2. アプリケーションを QueryClientProvider で囲む
function App() {
return (
<QueryClientProvider client={queryClient}>
<GithubSample />
<ReactQueryDevtools initialIsOpen={true} />
</QueryClientProvider>
)
}
// 3. 使用例
function GithubSample() {
// useQuery
// queryKey: 一意なキーを指定する
// queryFn: promise を返す関数を指定する
const { isLoading, isError, data, error, refetch } =
useQuery<RepositoriesResponse>({
queryKey: ['orgs'],
queryFn: () =>
fetch(
`https://api.github.com/search/repositories?q=org:tanstack&sort=stars`,
{
headers: {
Authorization: 'Bearer <TOKEN>',
},
},
).then((res) => res.json()),
})
// ローディング中
if (isLoading) {
return <span>Loading...</span>
}
// エラー発生時
if (isError) {
return <span>Error: {(error as any).message}</span>
}
// data は res.json() の結果にアクセスできる
return (
<div className="App">
<table border={1} style={{ borderCollapse: 'collapse' }}>
<thead>
<tr>
<th>No</th>
<th>name</th>
<th>description</th>
<th>createdAt</th>
<th>star</th>
<th>url</th>
</tr>
</thead>
<tbody>
{data &&
data.items.map((v: any, index: any) => (
<tr key={index}>
<td>{index + 1}</td>
<td>{v.name}</td>
<td>{v.description}</td>
<td>{new Date(v.created_at).toLocaleDateString()}</td>
<td>{v.stargazers_count}</td>
<td>{v.url}</td>
</tr>
))}
</tbody>
</table>
</div>
)
}
export default App
実行結果です。ちゃんと取れていますね。素晴らしい。
data
に型を指定するには useQuery<data型> で指定します(正確には data 以外にエラー型など複数の指定が可能です)。data 型を設定する場合、イメージはこんな感じになります。
type Repository = {
name: string
description: string
created_at: string
stargazers_count: string
url: string
...
}
type RepositoriesResponse = {
items: Repository[]
...
}
...
// これで data の型が RepositoriesResponse | undefined になります
const { isLoading, isError, data, error } = useQuery<RepositoriesResponse>(...)
余談 Github API の実行について
TOKENはこちらの手順で生成できます。
search API についてはこちらなどを見ることでパラメータが確認できます。
クエリ状態の確認: react-query 用 dev tool
Apollo Client DevTools のような、クエリ結果を確認するための dev tool も提供されています。インストールと組み込みも簡単です。
yarn add @tanstack/react-query-devtools
function App() {
return (
<QueryClientProvider client={queryClient}>
<GithubSample />
<ReactQueryDevtools initialIsOpen={true} />
</QueryClientProvider>
)
}
画面下にクエリで取得した情報と、キーに紐づく情報が確認できます。
パラメータ渡し
実際のサーバ通信処理ではパラメータを指定するケースの方が多いですが、その場合の方法。useQuery
の queryFn に指定する関数には、以下のような情報が渡されます(TanStack では QueryFunctionContext
という型)。queryKey
は名前の通り、useQuery
で指定されている queryKey
です。
ここでは、useState
で画面から oraganization の値を受け取り、queryKey
で指定されている key がこの名前となり、Github からデータ取得をしています。
/**
* queryFn に相当する部分を外だし
* queryKey で指定された変数を参照することが可能
*/
function getReposFn(param: QueryFunctionContext) {
const [_key, { key }] = param.queryKey as [string, { key: string }]
return fetch(
`https://api.github.com/search/repositories?q=org:${key}&sort=stars`,
{
headers: {
Authorization: 'Bearer <TOKEN>',
},
},
).then((res) => res.json())
}
// 3. 使用例
function GithubSample() {
const [key, setKey] = useState('tanstack')
// useQuery
// queryKey: 一意なキーを指定する
// queryFn: promise を返す関数を指定する
const { isLoading, isError, data, error, refetch } =
useQuery<RepositoriesResponse>({
queryKey: ['orgs', { key }],
queryFn: getReposFn,
})
///
param の値
なので、クエリを実行するときの動的なパラメータも含めて queryKey
のキーに指定することもでき、その情報をもとに、実際に情報を取得するクエリを実行することもできます(書き方によっては同じ queryKey
で別のパラメータで情報取得をするといったことももちろん可)。
画面側では organization を選べるようにしておきます。
return (
<div className="App">
{/* refetch で query を再実行する。このとき useState で定義した変数が最新の状態でクエリが行われる */}
<select
value={key}
onChange={async (e) => {
await setKey(e.target.value)
refetch()
}}
>
<option>tanstack</option>
<option>vuejs</option>
<option>facebook</option>
<option>angular</option>
</select>
...
以下、実行結果です。select で選んだ内容によって、setKey
で更新され、その後 refetch
で再度 queryFn
を実行しています。
実行タイミングの制御(手動トリガー)
Disabling/Pausing Queries を参照。
これまでの例だと読み込み時にリクエストが実行されますが、ボタン押下など何らかのユーザ操作等をトリガーに手動実行したい場合は、enabled
プロパティを false
または式にすることで制御が可能です。
前者は完全に手動で、公式でもバックグラウンドフェッチなど TanStack Query がもつ多くの機能が無効になるようです。後者は最初のトリガーだけ手動で後は自動にするパターンです。
任意のトリガーでクエリを実行
完全に false
に倒した場合です。
const { isInitialLoading, isLoading, isFetching, isError, data, error, refetch } =
useQuery<RepositoriesResponse>({
queryKey: ['orgs', { key }],
queryFn: getReposFn,
enabled: false,
})
この場合は画面上では「 Loading… 」の表示のみがでます。ちなみに、enabled: false の場合の各種データの状態は以下の通りで、isLoading が true
なので画面に 「Loading… 」がでています。
refetch()
を実行すると、enabled: false
でもクエリが実行されます。この場合の状態は以下です。
その後、冒頭の通り一覧が表示されます。最終的な状態は以下の通りです。
なお、この状態でもう一度 refetch()
を行った場合の、実行中の状態は以下になります。
そのため、公式にあるように、enabled: false の場合で利用する場合は、isInitialLoading
は 初めての取得中という判断はできないため、isLoading && isFetching
で初回か否かを判断することになります。
任意のトリガーでクエリを有効化
今度はデフォルト false で切り替えられるようにした場合です。
const [enableQuery, setEnableQuery] = useState(false)
const { isInitialLoading, isLoading, isError, data, error, refetch } =
useQuery<RepositoriesResponse>({
queryKey: ['orgs', { key }],
queryFn: getReposFn,
enabled: enableQuery,
})
初期状態は当然ですが enabled: false
のパターンと同じです。
ボタン押下で enabled を有効にさせると、状態は以下となります(手動 refetch() 時と同じ)。
その後、冒頭の通り一覧が表示されます。最終的な状態は以下の通りです(これも手動時と同じでした)。
まとめ
Tanstak Query は実際の取得方法(ライブラリ / 方式)はなんでもよく、それらがうまくラップされた IF が提供されている点でとても便利だなと感じました。
今回はほんの触りですが基本的な使い方は分かったので、今後は TanStack Query が持っているキャッシュなどの機能についてキャッチアップしていけば使っていけそうです。