この記事の目的
- Reactの理解とアウトプット、振り返り用
- Reactでよく使われている基本的な技術を言語化できるようする
- TanStack Query(React Query)について
概要
- TanStack Queryとは
- TanStack Queryインストール
- TanStack Query使用例
- まとめ
- 【番外】Tanstack Queryを使用しない場合
TanStack Query(React Queryとは
Tanstack Queryを簡単に説明するとサーバの状態(データ)をfetching, caching, synchronizing and updating(サーバ上のデータをフェッチ、キャッシュ、同期、更新)することができるdata fetchingライブラリです
簡単に言うと、APIとのやりとりをすごく楽にしてくれるライブラリのこと
因みにバージョン3まではReact Queryと呼ばれていましたがバージョン4になり名称がTanStack Queryに変更なり
TanStack QueryになったことでReactだけではなくSolid, Vue, Svelteなどでも利用できるみたいです。
TanStack Queryインストール
npm install -D @tanstack/react-query
TanStack Query使用例
実装内容
-
api/tasks
というタスク一覧が取得できるAPIから情報取得を行う -
resources/ts/pages/tasks/index.tsx
で取得処理記載
resources/ts/App.tsx
import React from 'react';
import { Router } from "./Router";
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false
},
mutations: {
retry: false
}
}
});
export const App = () => {
return (
<QueryClientProvider client={queryClient}>
<Router />
</QueryClientProvider>
)
}
設定できるオプションには以下のようなものがあるみたいです。一部抜粋
オプション名 | 説明 |
---|---|
retry | デフォルトで3回まで失敗してもフェッチをする設定になっていますが、falseを指定すれば無効化できる。 |
refetchOnWindowFocus | デフォルトでユーザーがブラウザのコンポーネントにフォーカスを当てた時に自動でフェッチが動くようになっている。 レンダリングが多く行われることになりかねないので、できればfalseにしておく。 |
suspense | trueにすることで、最初のデータ取得完了までSuspenseにフォールバックしてくれる。 そのため適当な粒度でSuspenseを差し込んでおけばデータがnullかどうかを気にせずほぼ同期的に扱える |
resources/ts/pages/tasks/index.tsx
import React from 'react';
import axios from 'axios';
import { useQuery } from '@tanstack/react-query';
type Task = {
id: number
title: string
is_done: boolean
created_at: Date
updated_at: Date
}
export const TaskPage = () => {
// 第一引数:キーとなる文字列
// 第二引数:非同期の処理
// useQueryから受け取るのはdate(APIから取得したタスクの一覧)とstatus(エラーやローディング情報)
const { data:tasks, status} = useQuery(['tasks'],async () =>{
const { data } = await axios.get<Task[]>('api/tasks')
return data
})
// 返却されたステータスとタスクの中身によって画面表示を変更
if (status === 'loading') {
return <div className="loader" />
} else if (status === 'error') {
return <div className="align-center">データの読み込みに失敗しました</div>
} else if (!tasks || tasks.length <= 0) {
return <div className="align-center">登録されたTodoはありません</div>
}
// ここから下は取得したタスクを画面表示しているだけです。
return (
<>
<div className="inner">
<ul className="task-list">
{ tasks.map(task => (
<li key={ task.id }>
<label className="checkbox-label">
<input type="checkbox" className="checkbox-input" />
</label>
<div><span>{ task.title }</span></div>
<button className="btn is-delete">削除</button>
</li>
))}
</ul>
</div>
</>
);
}
【番外】バージョンでのエラー
今回は@tanstack/react-query": "^4.24.10"
とバージョン4を使用しているのですが、下記のようなエラーが出るかもしれないです。
query.ts:359 As of v4, queryKey needs to be an Array. If you are using a string like 'repoData', please change it to an Array, e.g. ['repoData']
このエラーが出た際は
resources/ts/pages/tasks/index.tsx
const { data:tasks, status} = useQuery('tasks',async () =>{
上記を下記のようにしてください。バージョン4以降でuseQueryの第一引数の型が異なるみたいです。
resources/ts/pages/tasks/index.tsx
const { data:tasks, status} = useQuery(['tasks'],async () =>{
まとめ
- Tanstack Queryは前までReact Queryと呼ばれていた
- TanStack Queryは、サーバから取得したデータをクライアントでキャッシュとして保管できる
- 新しいデータを取得したときにいつキャッシュを更新するか、なども管理することができる
- queryClient:クエリとキャッシュを管理する
- QueryClientProvider:キャッシュやクライアント設定を下のコンポーネントに与えたり、値としてqueryClientを取得する
- useQuery():Hooksの一つ。クエリを行うことができる
- ReactQueryDevTool:TanStack Queryで管理しているデータの変化を確認するための開発者用ツール
【番外】Tanstack Queryを使用しない場合
resources/ts/pages/tasks/index.tsx
import React, { useEffect, useState } from 'react';
import axios from 'axios';
type Task = {
id: number
title: string
is_done: boolean
created_at: Date
updated_at: Date
}
export const TaskPage = () => {
const [tasks, setTasks] = useState<Task[]>([]);
const getTasks = async () => {
// 非同期の処理なのでaxiosの前にawaitを記載。axios.get()の引数に取得するAPIのパスを指定
// 分割代入で{ data }とすることで取得したい部分のみ取得
const { data } = await axios.get<Task[]>('api/tasks')
console.log(data);
setTasks(data);
}
useEffect(() => {
getTasks()
})
return (
<>
<div className="inner">
<ul className="task-list">
{ tasks.map(task => (
<li key={ task.id }>
<label className="checkbox-label">
<input type="checkbox" className="checkbox-input" />
</label>
<div><span>{ task.title }</span></div>
<button className="btn is-delete">削除</button>
</li>
))}
</ul>
</div>
</>
);
}