🧊

Tanstack Query 〜 Reusable な useQuery を目指して 〜

2025/01/02に公開

非同期処理の状態管理としてTanstack Queryを導入した際の活用方法についてです。

ポイントはこちら

  • useQueryをラップしたhooksをAPIエンドポイント毎に定義
  • useQueryのオプション(例:select, staleTimeなど)を外部から指定可能にする

以下は、useQueryをラップしてtodosエンドポイント専用のカスタムフックを作成する例です。
useQuery のオプション型からqueryKeyqueryFn を除外し、それ以外のオプションを呼び出し元で自由に指定できるようにしています。

/services/todo.query.ts
import { api } from "@/lib/http-client";
import { type UseQueryOptions, useQuery } from "@tanstack/react-query";

// api res
type TodoDto = {
  id: string;
  title: string;
};

export const fetcher = async (): Promise<TodoDto[]> => {
  const res = await api.get("/todos");
  return res.data;
};

export const useTodoQuery = <QueryResult>(
  config?: Omit<
    UseQueryOptions<TodoDto[], unknown, QueryResult>,
    "queryKey" | "queryFn"
  >
) => {
  return useQuery({
    queryKey: ["todos"],
    queryFn: fetcher,
    ...config,
  });
};

呼び出す側の実装はこのようになります。
実装例ではAPIレスポンスの整形処理をオプションとして指定しています。
ViewからはuseTodohooksを呼び出します。

/usecase/get-todo.ts
import { fetcher, useTodoQuery } from "../service/todo.query";

type TodoDto = Awaited<ReturnType<typeof fetcher>>;

const composeTodo = (res: TodoDto) => {
  return res.map((todo) => ({ id: todo.id, title: todo.title }));
};

export const useTodo = () => {
  return useTodoQuery({
    select: composeTodo,
    gcTime: 0,
  });
};

例えば、複数のコンポーネントが同じAPIエンドポイントにリクエストする場合、それぞれのコンポーネントに必要なデータの抽出を柔軟にカスタマイズすることも可能ですね。

これにより、APIエンドポイントごとに統一したデータ取得ロジックを持ちながら、必要に応じて柔軟にオプションを指定できます。

Discussion