🎃

ReactとNext.jsでTanstackQuery(旧React Query)の導入をやってみた

に公開

TanstackQueryとは?

TanstackQueryとは、データ取得する際に使われるライブラリです。
公式ドキュメントから引用するとWebアプリケーションでのサーバー状態の取得、キャッシュ、同期、更新が非常に簡単になります。

公式ドキュメント

公式ドキュメント
公式ドキュメント(概要)

インストール

React(npm)で進めて行きます。yarnを使っているならyarn、pnpmならpnpmでやってください。
コマンドが少し変わるので公式ドキュメントから確認してください。

npm i @tanstack/react-query

公式ドキュメントのインストール

従来のデータフェッチ(インターネット上から情報を取ってくること)のやり方

以前はuseEffectやuseStateなどを使っていました。

import { useState, useEffect } from 'react'
import axios from 'axios'

type Post = {
  id: number;
  title: string;
};

const Posts = () => {
  const [data, setData] = useState<Post[]>([]);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    const fetchPosts = async () => {
      try {
        setIsLoading(true);
        const response = await axios.get("***********");
        setData(response.data);
        setError(null);
      } catch (err) {
        if (err instanceof Error) {
          setError(err.message);
        } else {
          setError('不明なエラーが発生しました');
        }
      } finally {
        setIsLoading(false);
      }
    };

    fetchPosts();
  }, []);

  if (isLoading) return <div>読み込み中...</div>;
  if (error) return <div>エラー: {error}</div>;

  return (
    <div>
      <h1>投稿一覧</h1>
      <div>
        {data.map((post) => (
          <p key={post.id}>
            <a href={`/posts/${post.id}`}>{post.title}</a>
          </p>
        ))}
      </div>
    </div>
  );
};

export default Posts;

従来のやり方の問題点

上記のコードくらいのuseStateやuseEffectの数なら、そんなに大した問題ではないのですが、
実際に稼働しているWebサービスだったらどうでしょうか?

大規模アプリで発生する問題

1.同じコードの繰り返し
APIからデータを取得するたびに、毎回以下を書く必要があります。💦

const [data, setData] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => { /* データ取得処理 */ }, []);

APIが10、20、100と増えていくたびに、このコードも同じだけ増えていきます。😱

2.重複したデータ取得

//Postsページ → ユーザー情報を取得
// Profileページ → 同じユーザー情報をまた取得
// Headerコンポーネント → また同じユーザー情報を取得
// → 無駄なAPI呼び出しが大量発生!

3.キャッシュ管理が大変

一度取得したデータを再利用したいけど、データが古くなったら再取得したい
この管理を自分でやるのは非常に大変😅

4.状態管理の複雑化
グローバルな状態管理を入れると、状態管理が複雑化してメンテナンス性が悪くなってしまいます。
そこに大量のuseStateとuseEffectを書くと、どこで何を管理しているのか分からなくなります。😭
コードが肥大化してぐちゃぐちゃになって行きます。

これらを解決するために生まれたのが
TanstackQueryです。

TanstackQueryでのデータフェッチ

サーバーからデータを取ってくる作業をめちゃくちゃ楽にしてくれるライブラリです。
queryClientをインスタンス化して、QueryClientProviderでComponentを囲みます。

import './App.css'
import Posts from './components/Posts'
import {
  QueryClient,
  QueryClientProvider,
} from '@tanstack/react-query'

const queryClient = new QueryClient() // queryClientをインスタンス化

function App() {
  // QueryClientProviderでComponentを囲む
  return ( 
    <QueryClientProvider client={queryClient}>
      <Posts />
    </QueryClientProvider>
  )
}

export default App

公式ドキュメントの概要

useQuery

データを取得する時(GET) に使います。

// 例:記事一覧を取得
const { data, isLoading, error } = useQuery({
  queryKey: ['articles'],
  queryFn: () => fetch('/api/articles').then(res => res.json()),
});

公式ドキュメントuseQuery

useMutation

データを変更する時に使います。(POST/PUT/DELETE)

// 例:いいねする
const mutation = useMutation({
  mutationFn: () => fetch(`/api/articles/${id}/like`, { method: 'POST' }),
  onSuccess: () => {
    // 成功後の処理
  },
});
// 実行
mutation.mutate();

導入メリット

1.コードがシンプルになる

useStateやuseEffectを自分で書く必要がなく。
ローディング、エラー、データの状態を自動管理

2.自動キャッシュ機能
一度取得したデータは自動でキャッシュされ、
同じデータを複数のコンポーネントで使っても、API呼び出しは1回だけで済みます。

3.自動で再取得
データが古くなったら自動で再取得して、
ウィンドウにフォーカスが戻ったら最新データを取得します。

4.保守性の向上
データ取得のロジックが一箇所に集約するので、大規模アプリでも管理しやすいです。

結論(TanStack Queryはなぜ生まれたか)

大規模なアプリで発生する「無駄なデータ取得」と「煩雑な状態管理」を解決するために生まれました。

参考動画

useEffectよりも優れたデータフェッチング方法【TanstackQuery入門】

説明

https://tanstack.com/

補足

ClaudeCodeにも文章を書くの少し手伝ってもらっています。

Discussion