React Queryを使用してisLoadingとisFetchingでUIを切り替える

# はじめに
本記事ではreact-queryを使用して非同期処理中のステータスを取得し、画面の表示内容を切り替える方法を実装していきます。


# ゴール
react-queryを使用して非同期処理中のステータス（isLoading、isFetching)を取得し、各ステータスに応じて画面の表示内容を切り替える。


# React Query のインストール

```
npm install @tanstack/react-query
```

https://www.npmjs.com/package/@tanstack/react-query


# API からデータを取得して表示する

以下のスクリプトでは、 `react-query` を使用し疑似的なAPIコールに2秒かかるような処理を作成します。
APIコール中の非同期処理ステータス（isLoading や isFetching）を取得し、ステータスに応じて画面の表示内容を切り替えます。

```tsx:App.tsx
import { useQuery } from '@tanstack/react-query';

type UserData =  {
  name: string;
  email: string;
}

// 疑似的にAPIコールを再現（2秒待つ）
const fetchData = async (): Promise<UserData> => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({
        name: 'ほげほげ太郎',
        email: 'hoge1@fuga2'
      });
    }, 2000);
  });
};

const App: React.FC = () => {
  const { data, isLoading, isFetching } = useQuery<UserData, Error>({
    queryKey: ['dummyData'],
    queryFn: fetchData,
  });

  // クエリの状態を確認
  console.log('Query State:', {
    data,           // 取得したデータ
    isLoading,      // 初回ローディング中
    isFetching,     // データ取得中か（再取得含む）
  });

  // 初回取得
  if (isLoading) {
    return <div>データを取得中です...</div>;
  }

  // データの取得
  if (isFetching) {
    return <div>データを更新中です...</div>;
  }

  return (
    <div>
      <h1>ユーザー情報</h1>
      {data && (
        <div>
          <p>名前: {data.name}</p>
          <p>メール: {data.email}</p>
        </div>
      )}
    </div>
  );
};

export default App;
```

<details><summary>main.tsx</summary>

ルートでQueryClientProviderを設定します。

```tsx:main.tsx
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import './App.css'
import App from './App.tsx'

const queryClient = new QueryClient()
const rootElement = document.getElementById('root')

createRoot(rootElement as HTMLElement).render(
  <StrictMode>
    <QueryClientProvider client={queryClient}>
      <App />
    </QueryClientProvider>
  </StrictMode>,
)
```

</details>


:::note info

useQuery には `isLoading` や `isFetching` 以外にも、`fetchStatus` や `error` などさまざまなステータスが用意されています。詳細は下記のドキュメントの通りです。



https://tanstack.com/query/latest/docs/framework/react/reference/useQuery

:::

## 動作の確認

### 初回ロードディング時

```
{
  data: undefined,
  error: null,
  isLoading: true,
  isFetching: true,
  isError: false
}
```

### 初回ロードディング完了後

```
{
  data: { name: 'ほげほげ太郎', email: 'hoge@example.com' },
  error: null,
  isLoading: false,
  isFetching: false,
  isError: false
}
```

![sample_load_t1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1247619/07335625-e31c-5ecb-6a4b-d074a1ad3b60.gif)



### 別タブから戻ってきた時の再フェッチ中

```
{
  data: {
    name: 'ほげほげ太郎',
    email: 'hoge1@fuga2'
  },
  error: null,
  isLoading: false,
  isError: false,
  isFetching: true
}
```

### 再フェッチ後の処理

```
{
  data: {
    name: 'ほげほげ太郎',
    email: 'hoge1@fuga2'
  },
  error: null,
  isLoading: false,
  isError: false,
  isFetching: false
}
```

![sample_load_t2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1247619/637b59d8-23b9-9be9-958b-0d946218d0c8.gif)

:::note info
React Query DevTools を使用したモニタリング

React Query DevToolsを使用することで、クエリ情報を確認できます。


```tsx
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'

const App: React.FC = () => {
  return (
    <>
      {/* 既存のコンポーネント */}
      {import.meta.env.DEV && <ReactQueryDevtools initialIsOpen={false} />}
    </>
  );
};
```

`initialIsOpen={false}` だとデフォルトでDevToolsパネルは閉じた状態で表示されます。
右下のアイコンを押下すると、パネルが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1247619/6bbb05af-7005-41ca-3919-78d6f7114fdb.png)


https://www.npmjs.com/package/@tanstack/react-query-devtools

:::


# 参考

https://tanstack.com/query/latest/docs/framework/react/reference/useQuery