🐡

TanStack Query の導入

2024/03/31に公開

はじめに

今回のコードはこちら↓
https://github.com/49takaya3989/sample_tanstack-query

開発環境

"@tanstack/react-query": "^5.28.6",
"@tanstack/react-query-devtools": "^5.28.10",
"@tanstack/eslint-plugin-query": "^5.28.6",

TanStack Query とは?

TanStack Query (FKA React Query) は、Web アプリケーションに不足しているデータを取得するライブラリとしてよく説明されますが、より技術的に言えば、 Web アプリケーションでのサーバー状態の取得、キャッシュ、同期、更新を簡単に行うことができます。
ほとんどのコア Web フレームワークには、全体的な方法でデータを取得または更新するための独自の方法が付属していません。このため、開発者はデータ取得に関する厳密な意見をカプセル化したメタフレームワークを構築するか、独自のデータ取得方法を発明することになります。これは通常、コンポーネントベースの状態と副作用を組み合わせたり、より汎用的な状態管理ライブラリを使用してアプリ全体に非同期データを保存および提供したりすることを意味します。

https://tanstack.com/query/latest/docs/framework/react/overview

巷ではよく、 TanStack Query はデータフェッチライブラリであると言われているが、
公式やメンテナーのDominikさんも言及されているように、主な目的は、効率的な非同期状態の管理である。

導入

今回は練習としてよく使われる TODO アプリを作っていく。
DB は Firebase で行うが、Firebase の構築やルーティングに関する説明は行わないので、わからない方はChatGPTに聞いてみてください。
下記のようなプロンプト(Reactの環境構築は終わっている前提)を投げれば教えてくれます。

react + firebseのコード教えて。
reactの構築は終わってます。
  1. 必要なパッケージのインストール
  2. Eslint に設定を追加
  3. TanStack Query をアプリケーションに接続
  4. 実装

必要なパッケージのインストール

公式を参考に進めていく。
今回は下記のパッケージをインストールする。

npm i @tanstack/react-query @tanstack/react-query-devtools
npm i -D @tanstack/eslint-plugin-query

@tanstack/eslint-plugin-query:eslint でタイポなどを防ぐためのライブラリ。
@tanstack/react-query-devtools:TanStack Query の状態を確認するための Devtool ライブラリ。

Eslint に設定を追加

module.exports = {
  ・・・
  extends: [
    ・・・
+   'plugin:@tanstack/eslint-plugin-query/recommended',
  ],
}

TanStack Query をアプリケーションに接続

まずは、接続するのに必要なクライアントを作成する。

const queryClient = new QueryClient()

これだけです!
次に、上記で作成したクライアントを Provider と一緒に定義します。

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <QueryClientProvider client={queryClient}>
      <App />
    </QueryClientProvider>
  </React.StrictMode>,
)

これでアプリケーションとの接続は完了!
これに加えて今回は、TanStack Query の状態を確認できる Devtool を使いたいので、コードを追加します。

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <QueryClientProvider client={queryClient}>
      <App />
+     <ReactQueryDevtools initialIsOpen={false} />
    </QueryClientProvider>
  </React.StrictMode>,
)

実装

基本的に API のリクエスト関係で使う関数は、useQueryuseMutationなのでこの2つに絞って実装する。
細かい説明は公式や参考記事に記載がたくさんあるので、今回はこう書けば使えるということを目的に説明していく。

interface Task {
  id: string;
  task: string;
  completed: boolean;
}

// useQuery
export const useLoadTasks = () => {
  const { data, isPending } = useQuery({
    queryKey: ["task", "list"],
    queryFn: async () => {
      await getDocs(tasksCollectionRef)
        .then(res => res.docs.map(el => (
          {...el.data(), id: el.id} as Task
        )));
    },
  })

  return { data, isPending }
}

// useMutation
export const useCreateTask = () => {
  const mutateCreate = useMutation({
    mutationFn: async (newTask: string) =>
      await addDoc(tasksCollectionRef, { task: newTask, completed: false });
  })

  return { mutateCreate }
}

const App: React.FC = () => {
  const { data, isPending } = useLoadTasks()
  const { mutateCreate } = useCreateTask()
  const [newValue, setNewValue] = useState('')
  ・・・
  const submitHandler = useCallback(
    (e: FormEvent<HTMLFormElement>) => {
      e.preventDefault();
      mutateCreate.mutate(
        newValue,
      );
    },
    [mutateCreate, newValue]
  )
  ・・・
  return (
    <div>
      <form onSubmit={submitHandler}>
        <input
          type="text"
          value={newValue}
          onChange={(e) => setNewValue(e.target.value)}
          placeholder="Add a new task"
        />
        <button>Add Task</button>
      </form>
      {isPending
        ? '...loading'
        : (
          <div>
            {data && data.map((task) => <div key={task.id}>・・・</div>)}
          </div>
        )
      }
    </div>
  );
}

export default App;

useQuery には、 queryKey と queryFn 2つのプロパティ、 useMutation には queryFn 1つのプロパティが必要となる。
queryKey
キャッシュを管理するために使用されるもの。ユニークなものにする必要がある。
queryFn
APIへリクエストするための関数。

画面右下に表示されている↓のようなアイコンをクリックして TanStack Query の状態を確認する。

アイコンをクリックすると、 queryKey に紐づいたキャッシュが表示される。

(応用)カスタマイズ

基本的には上記で動くようになるが、コード管理がやりづらいので切り分けていく。
まず初めに、 API を叩くためのディレクトリとして /service/task を作成する。

mkdir src/service/task

作成したディレクトリの中に、5つのファイルを作成する。

cd src/service/task && touch function.ts index.ts key.ts selector.ts type.ts

function.ts
mutationFn に記載する、APIを叩くための関数を管理するためのファイル。
index.ts
useQuery や useMutation を使った hooks を作成するファイル。
key.ts
queryKey に記載する key を管理するためのファイル。
selector.ts
今回紹介はしていないが、 useQuery には select オプションがあり、そこに定義する関数を管理するファイル。
type.ts
上記関数で使用する型を定義するためのファイル。

上記5つのファイルにコードをそれぞれ切り分ける。

function.ts
index.ts
key.ts
type.ts

以上!

参考記事
https://zenn.dev/taisei_13046/books/133e9995b6aadf/viewer/2ce93a
https://tanstack.com/query/latest/docs/framework/react/community/tkdodos-blog

Discussion