← 記事一覧に戻る

TanStack Query入門

#React #TanStack Query

はじめに

  • TanStack Query(旧React Query)は、非同期データの取得・キャッシュ・同期を簡単に行えるライブラリです
  • useEffectとuseStateを使った従来のデータフェッチの複雑さを解消してくれます
  • React以外にも、Vue、Solid、Svelteなど様々なフレームワークに対応しています

なぜTanStack Queryを使うのか

従来のデータフェッチの問題点

useEffectとuseStateを使った従来のパターンでは、多くのボイラープレートが必要です:

function UserProfile({ userId }: { userId: string }) {
  const [user, setUser] = useState<User | null>(null);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    setIsLoading(true);
    fetch(`/api/users/${userId}`)
      .then((res) => res.json())
      .then((data) => {
        setUser(data);
        setIsLoading(false);
      })
      .catch((err) => {
        setError(err);
        setIsLoading(false);
      });
  }, [userId]);

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;
  return <div>{user?.name}</div>;
}

このパターンには以下の問題があります:

  • ローディング・エラー・データの3つの状態を手動で管理する必要がある
  • キャッシュがないため、同じデータを何度もフェッチしてしまう
  • コンポーネントがアンマウントされた後にstateを更新しようとする問題
  • 複数のコンポーネントで同じデータを使う場合の共有が難しい

TanStack Queryで解決

TanStack Queryを使うと、上記のコードはこうなります:

function UserProfile({ userId }: { userId: string }) {
  const { data: user, isLoading, error } = useQuery({
    queryKey: ['user', userId],
    queryFn: () => fetch(`/api/users/${userId}`).then((res) => res.json()),
  });

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;
  return <div>{user?.name}</div>;
}

インストール

npm install @tanstack/react-query

セットアップ

アプリケーションのルートでQueryClientProviderを設定します:

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

const queryClient = new QueryClient();

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <YourApp />
    </QueryClientProvider>
  );
}

基本的な使い方

useQuery - データの取得

useQueryはデータを取得するための基本的なフックです:

import { useQuery } from '@tanstack/react-query';

function TodoList() {
  const { data, isLoading, error } = useQuery({
    queryKey: ['todos'],
    queryFn: async () => {
      const response = await fetch('/api/todos');
      return response.json();
    },
  });

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error occurred</div>;

  return (
    <ul>
      {data.map((todo) => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  );
}

queryKeyについて

  • queryKeyはクエリを一意に識別するためのキーです
  • 配列形式で指定し、依存する値を含めることでキャッシュを適切に管理できます
  • 例:['todos']['todo', todoId]['todos', { status: 'done' }]

useMutation - データの更新

useMutationはデータの作成・更新・削除に使用します:

import { useMutation, useQueryClient } from '@tanstack/react-query';

function AddTodo() {
  const queryClient = useQueryClient();

  const mutation = useMutation({
    mutationFn: (newTodo: { title: string }) => {
      return fetch('/api/todos', {
        method: 'POST',
        body: JSON.stringify(newTodo),
      });
    },
    onSuccess: () => {
      // 成功時にtodosのキャッシュを無効化して再取得
      queryClient.invalidateQueries({ queryKey: ['todos'] });
    },
  });

  return (
    <button
      onClick={() => mutation.mutate({ title: 'New Todo' })}
      disabled={mutation.isPending}
    >
      {mutation.isPending ? 'Adding...' : 'Add Todo'}
    </button>
  );
}

キャッシュの設定

TanStack Queryの強みはキャッシュ管理です。主要なオプションを紹介します:

const { data } = useQuery({
  queryKey: ['todos'],
  queryFn: fetchTodos,
  staleTime: 1000 * 60 * 5, // 5分間はキャッシュを新鮮とみなす
  gcTime: 1000 * 60 * 30,   // 30分間キャッシュを保持(旧cacheTime)
});
  • staleTime: データが「古い」とみなされるまでの時間。この間は再フェッチしない
  • gcTime: 使われなくなったキャッシュがガベージコレクションされるまでの時間

DevToolsの活用

開発時にはDevToolsを使うとキャッシュの状態を可視化できます:

npm install @tanstack/react-query-devtools
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <YourApp />
      <ReactQueryDevtools initialIsOpen={false} />
    </QueryClientProvider>
  );
}

まとめ

  • TanStack Queryを使うことで、非同期データの取得・キャッシュ管理が大幅に簡単になります
  • useQueryでデータ取得、useMutationでデータ更新を行います
  • queryKeyを適切に設定することで、効率的なキャッシュ管理が可能です
  • DevToolsを活用して、キャッシュの状態をデバッグできます

参考