React Query を導入するタイミングはいつ？

フロントエンド開発で React を使うと、
グローバルステート管理 や データ取得 が悩みの種になりがちです。
シンプルな `useEffect` だけで API を叩く場合でも、エラー処理やローディング状態、ページネーションが絡むと一気にコードが増え、可読性も下がってしまいます。

そんなとき **React Query** が “頼れる相棒” になってくれます。この記事では、React Query の特徴と使いどころ、そしてシンプルな `useEffect` で済ませるケースとの見極め方を紹介します。

## React Query が提供する 6 つの強み

1. 宣言的なデータ取得：`useQuery` で「どう取るか」より「何を取りたいか」を書くイメージで実装できる
1. 自動キャッシュ：同じクエリを叩くたびに API を呼ばず、パフォーマンスが向上
1. リアルタイム更新：キャッシュが更新されると UI も即反映
1. クエリの無効化：必要に応じてキャッシュを捨てて再フェッチできる
1. 楽観的 UI 更新：サーバー確認前に UI を更新し、体感速度アップ
1. ローディング & エラー管理：状態管理用ボイラープレートを大幅に削減

## React Query の基本概念 — useQuery と useMutation

### `useQuery`（読み取り）

```
import { useQuery } from 'react-query';

const ExampleComponent = () => {
  const { data, isLoading, error } = useQuery('exampleQuery', fetchDataFunction);

  if (isLoading) return <p>Loading...</p>;
  if (error) return <p>Error: {error.message}</p>;

  // Render your component with the fetched data
  return <div>{data}</div>;
};
```

- クエリキー：'exampleQuery'
これがキャッシュの識別子になります。
- フェッチ関数：fetchDataFunction
Promise を返す関数なら何でも OK。

### `useMutation`（書き込み）

```
import { useMutation } from 'react-query';

const ExampleComponent = () => {
  const { mutate } = useMutation('exampleMutation', updateDataFunction);

  const handleClick = () => {
    // Trigger the mutation when a button is clicked
    mutate();
  };

  return <button onClick={handleClick}>Update Data</button>;
};
```

- 変更系 API を呼び出すときに使用。
- 成功後に関連クエリを invalidates して最新データを取得。

## React Query の魔法 — カスタムページネーション

API 側で `limit` / `offset` などを使う場合、
**React Query** の `fetchNextPage` と `getNextPageParam` が威力を発揮します。

```
import { useQuery, useQueryClient } from 'react-query';

const LIMIT = 10;

const fetchPaginatedData = async ({ pageParam = 1 }) => {
  const response = await fetch(`/api/data?page=${pageParam}&limit=${LIMIT}`);
  return response.json();
};

const CustomPaginationComponent = () => {
  const queryClient = useQueryClient();

  const { data, fetchNextPage, hasNextPage } = useQuery(
    'paginatedData',
    fetchPaginatedData,
    {
      getNextPageParam: (lastPage, allPages) => {
        // Use the current length of all pages to calculate the next page
        return allPages.length + 1;
      },
    }
  );

  const handleLoadMore = () => {
    fetchNextPage();
  };

  return (
    <div>
      {/* Render your paginated data */}

      {hasNextPage && <button onClick={handleLoadMore}>Load More</button>}
    </div>
  );
};
```

### ポイント

- `fetchPaginatedData` は `pageParam` を受け取り、API に `offset` と `limit` を渡す。
- `getNextPageParam` で「次に呼ぶ offset」を計算。
- `hasNextPage` が `true` なら「もっと見る」ボタンを表示。

これだけで無限スクロールや手動ロードがシンプルに書けます。

## React Query を導入すべきか？ `useEffect` で十分か？

### React Query を使うメリット
- 複雑なデータ取得ロジック を簡潔に書ける
- 自動キャッシュ により通信回数を削減
- ローディング／エラー／ページネーション の共通処理を省力化
- リアルタイム更新 で UI 一貫性を保てる
### デメリット
- ライブラリ学習コストが少し発生
- シンプルな画面には “やり過ぎ” になることも
### こんなときは React Query
- API が複数あり、依存関係 や 再取得 タイミングが複雑
- ユーザビリティ重視で 楽観的更新 や 無限スクロール が必要
- コーポレートサイトより SaaS アプリ のような場面
### こんなときは useEffect
- 1 画面 1 回しか呼ばない静的データ
- 成功・失敗・ローディングだけ分かれば十分

## まとめ

React Query は 「書きたいロジック」 に集中させてくれる便利ツールです。
API 通信が増えるにつれ恩恵が大きくなるので、プロジェクトの規模や要件に合わせて導入を検討しましょう。

### 参考リンク
- [React Query 公式ドキュメント](https://tanstack.com/query/v3/)
- [React Query 完全ガイド（Refine Blog）](https://refine.dev/blog/react-query-guide/)
- [React Query とキャッシュ戦略（Voskan Blog）](https://voskan.host/2023/03/01/react-query/)