【React】useQueryについて

# この記事の目的
- 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インストール
```zsh
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 |	デフォルトでユーザーがブラウザのコンポーネントにフォーカスを当てた時に自動でフェッチが動くようになっている。<br> レンダリングが多く行われることになりかねないので、できればfalseにしておく。|
| suspense | 	trueにすることで、最初のデータ取得完了までSuspenseにフォールバックしてくれる。<br> そのため適当な粒度で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"`とバージョン４を使用しているのですが、下記のようなエラーが出るかもしれないです。
```
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 () =>{
```
上記を下記のようにしてください。バージョン４以降で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>
        </>
    );
}
```