viviONグループでは、DLsiteやcomipoなど、二次元コンテンツを世の中に届けるためのサービスを運営しています。
ともに働く仲間を募集していますので、興味のある方はこちらまで。
🔰 はじめに
Reactのデータフェッチライブラリ色々あるけど何がいいんだろう🤔と思って調べていた時に、TanStack Query(旧React Query)を勧められたので、備忘も兼ねてまとめてみました!
🚀 TanStack Queryとは
- TanStack Queryとは、データの非同期取得及び更新・状態管理・キャッシュの機能を内包しているライブラリ
- React/Solid/Vue/Svelteなど殆どのフレームワークで使える(React Nativeも対応しているらしい)
- APIサーバからのレスポンスを状態管理することができる
- SWRよりもキャッシュの管理に関して細かく設定できる利点がある
🔬 実装例
📝 データの取得(GET)
useQuery
というフックを使用することで、データのフェッチを行うことができます。
import {
useQuery,
useMutation,
useQueryClient,
QueryClient,
QueryClientProvider,
} from '@tanstack/react-query'
import { getTodos, postTodo } from '../my-api'
// QueryClientのインスタンスを作成し、QueryClientProviderに渡す(QueryClient自体はキャッシュで使用する)
const queryClient = new QueryClient()
function App() {
return (
<QueryClientProvider client={queryClient}>
<Todos />
</QueryClientProvider>
)
}
function Todos() {
// useQueryでデータを取得
const { isLoading, isError, data, error } = useQuery({
// queryKeyでキーを指定する。キャッシュ管理などに使用する
queryKey: ['todos'],
// queryFnでデータ取得用の関数を入れる
queryFn: async() => {
const { data } = await axios.get(
'https//example.api/todos'
)
return data
},
// staleTimeにキャッシュが古くなるまでの時間を指定できる。デフォルトでは0(即古いとみなす)
staleTime: 1000,
// ウィンドウにフォーカスが当たったら再取得なども可能
refetchOnWindowFocus: true,
})
// isLoadingでローディング状態を取得
if (isLoading) {
return <LoadingSpinner />
}
// isErrorでエラーかを取得
if (isError) {
showErrorDialog()
}
return (
<div>
<ul>
{data.map((todo) => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
</div>
)
}
render(<App />, document.getElementById('root'))
🚨v5だと、Suspense利用時にuseQueryが使用できないので注意。 useSuspenseQuery()
, useSuspenseInfiniteQuery()
, useSuspenseQueries()
を必要に応じて使用していく。
📝 データの更新(POST)
useMutation
というフックを使用することで、データの更新を行うことができます。
import {
useQuery,
useMutation,
useQueryClient,
QueryClient,
QueryClientProvider,
} from '@tanstack/react-query'
import { getTodos, postTodo } from '../my-api'
const queryClient = new QueryClient()
function App() {
return (
<QueryClientProvider client={queryClient}>
<Todos />
</QueryClientProvider>
)
}
function Todos() {
const queryClient = useQueryClient()
// useQueryでデータを取得
const query = useQuery({
queryKey: ['todos'],
queryFn: async() => {
const { data } = await axios.get(
'https//example.api/todos'
)
return data
},
})
// useMutationでデータの更新を行なう
const mutation = useMutation({
// mutationFnにデータ更新用の関数を入れる
mutationFn: async({id, title}) => {
const { data } = await axios.post(
'https//example.api/todos',
{
id,
title
}
)
},
// onSuccessで更新成功時の処理を記載することができる
onSuccess: (result) => {
// queryClient.setQueryDataを使用することでキャッシュの値を更新することができる
queryClient.setQueryData(['todos'], prev => [...prev, result])
}
})
return (
<div>
<ul>
{query.data?.map((todo) => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
<button
onClick={() => {
mutation.mutate({
id: 1,
title: 'タイトル',
})
}}
>
Add Todo
</button>
</div>
)
}
render(<App />, document.getElementById('root'))
📈 TanStack Queryを使用することによるメリット
以前とあるプロジェクトにて、SWRを使用してデータフェッチの部分を開発していたのですが、SWRと比較するとキャッシュ部分に関してかなり細かいところまで詰めることができるなと言った印象です。
👍 useQueryのオプションで、StaleTime
とCacheTime
を設定することができるため、ページに応じて、適切なキャッシュ時間を細かく設定することができる
👍 更新頻度が高いページ(リアルタイムにデータの更新が必要なものなど)はStaleTimeを0にして、更新頻度が低いページ(CMSで管理しているものなど)やAPIの処理が重いページはStaleTimeを大きめに設定するなど、適切なキャッシュ設定をすることで、サイト全体のパフォーマンスを高めることができる
👍 useQueryのオプションが便利
enabled
enabledがtrueの時のみクエリを実行する
例えば認証完了時にのみ実行したいなど、特定のstateの状態を監視して実行するか分岐させたい時に使える
const { isLoading, isError, data, error } = useQuery({
queryKey: ['todos'],
queryFn: getTodos
// isAuthenticatedがtrueの時のみクエリを実行
enabled: isAuthenticated
})
select
selectをつけることで、取得したデータのfilteringをすることができる
const {data} = useQuery({
queryKey: ['todos'],
queryFn: getTodos,
// isCheckedがtrueの要素のみ取得している
select: (data) => data.map((todo) => todo.isChecked)
)
💬 所感
個人的には、以下のような条件で適宜プロジェクトに合わせて導入するのが望ましいかと思いました!!
プロジェクト自体がミニマムで、データフェッチがメインでキャッシュはそれほど重視しない => SWR
データフェッチに加えて、ページのキャッシュ部分をもっと突き詰めたい => TanStack Query
🔗 参考文献
- https://dev.classmethod.jp/articles/resolved-tanstack-query-v5-undefined-type-issue/
- https://zenn.dev/akfm/articles/react-state-scope
- https://zenn.dev/knowledgework/articles/607ec0c9b0408d?redirected=1
🌈 一緒に二次元業界を盛り上げていきませんか?
株式会社viviONでは、フロントエンドエンジニアを募集しています。
また、フロントエンドエンジニアに限らず、バックエンド・SRE・スマホアプリなど様々なエンジニア職を募集していますので、ぜひ採用情報をご覧ください。