フロントエンド開発で React を使うと、
グローバルステート管理 や データ取得 が悩みの種になりがちです。
シンプルな useEffect だけで API を叩く場合でも、エラー処理やローディング状態、ページネーションが絡むと一気にコードが増え、可読性も下がってしまいます。
そんなとき React Query が “頼れる相棒” になってくれます。この記事では、React Query の特徴と使いどころ、そしてシンプルな useEffect で済ませるケースとの見極め方を紹介します。
React Query が提供する 6 つの強み
- 宣言的なデータ取得:
useQueryで「どう取るか」より「何を取りたいか」を書くイメージで実装できる - 自動キャッシュ:同じクエリを叩くたびに API を呼ばず、パフォーマンスが向上
- リアルタイム更新:キャッシュが更新されると UI も即反映
- クエリの無効化:必要に応じてキャッシュを捨てて再フェッチできる
- 楽観的 UI 更新:サーバー確認前に UI を更新し、体感速度アップ
- ローディング & エラー管理:状態管理用ボイラープレートを大幅に削減
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 通信が増えるにつれ恩恵が大きくなるので、プロジェクトの規模や要件に合わせて導入を検討しましょう。