🙆

TanStack Query(旧ReactQuery)を使って感じたメリットをまとめてみた

2023/10/08に公開

はじめに

最近プロジェクトにTanStack Queryを導入しました。
実際に使ってみて感じたメリットをまとめてみました。
https://tanstack.com/

TacStackQueryとは?

データの取得とその状態管理を効率的に行えるライブラリです。
fetch処理をTanStack Queryでラップする事でpromiseの状態をフロントエンドフレームワーク形式の状態で返却されるため、フロントエンドフレームワークでの制御が行いやすくなります。
また、fetchしたレスポンスをキャッシュに格納する仕組みが用意されているので、簡単にリクエストを減らする事ができます。

フロントエンド実装者のメリット

インタラクティブなUIが簡単に実装できる。

UIを実装している人なら、ユーザーのことを考えて、スピナーなどを使用して状態を視覚的に表示してストレスを軽減させたいと考えると思います。
TanStackQueryを使う事でこの実装が簡単に行えるようになります。
fetchをTacStackQueryでラップするだけで、promiseの状態をフレームワークの状態に合わせて返してくれるので、ローディングやエラー時の実装が簡単に行なえます。

以下は、fetchをTanStackQueryでラップしてコンポーネント側で呼び出したサンプルです。

import { useQuery } from "@tanstack/vue-query"

type Response = {
  isLogined: boolean
  isYoutubeAuthroized: boolean
  user: { id: number; name: string }
}

export const UseGetStatus = () => {
  return useQuery({
    queryKey: ["GET_STATUS"],
    queryFn: async (): Promise<Response> => {
      const response = await fetch("https://server/api/v1/status")
      if (!response.ok) {
        throw new Error("Network response was not ok")
      }
      return response.json()
    },
    staleTime: Infinity,
    retry: 0
  })
}
<script setup lang="ts">
import { UseGetStatus } from "./use-get-status"
const getStatus = UseGetStatus()
</script>

<template>
  <div>
    <div v-if="getStatus.isFetching.value">....Fetching</div>
    <div v-if="getStatus.isError.value">Errorです。</div>
    <div v-if="getStatus.isSuccess.value">
      <p>ユーザー情報</p>
      <div>
        <p>userId: {{ getStatus.data.value?.user.id }}</p>
        <p>name: {{ getStatus.data.value?.user.name }}</p>
      </div>
    </div>
  </div>
</template>

個人情報の後処理が楽ちん

fetchでデータを取得するとその情報をクライアント側で保持する必要があります。
そのためログアウト後にユーザーに関する状態をすべて削除しなくてはなりません。
mpa構成のアプリケーションならサーバーサイドのフレームワーク側でよしなにやってくれますが、spa構成のアプリケーションである場合は手動で削除する必要があります。
削除されずに残ってしまうと、別ユーザーで再ログインした場合などに情報の不整合が生じ、危険な状態になります。

TanStack QueryですべてのAPIの状態を管理していると、APIの状態管理方法が統一されるので後始末がしやすいと感じました。

以下はログアウト後に、ユーザープロフィールと請求情報の状態を消すサンプルです。

import { useQueryClient, useMutation } from "@tanstack/vue-query"

export const usePostAuthenticationLogout = () => {
  const queryClient = useQueryClient()

  const mutation = useMutation({
    mutationFn: () => {
      return fetch("https://server/api/v1/authentication/logout", {
        method: "POST",
        headers: { "Content-Type": "application/json" }
      }).then((res) => res.json())
    },
    // ログアウトの成功、失敗に関わらず実行する
    onSettled: async () => {
      // プロフィールを初期化
      await queryClient.resetQueries({
        queryKey: ["GET_USER_PROFILE"],
        exact: true
      })
      // 請求情報を初期化
      await queryClient.resetQueries({
        queryKey: ["GET_INVOICE"],
        exact: true
      })
    }
  })
  return mutation
}

コンポーネント側で考える事が少ない。

APIの状態や後処理をすべてTanStanckQuery側で管理すると、コンポーネントが単純になります。
APIの状態はTanStack Query側ですべて行ってくれますし、個人情報等の後始末もTanStack Queryの方でやる事になります。
となると、API側の処理や状態はすべてTanStack Queryで作られたAPI層にまかせられ、コンポーネント側は欲しいデータを欲しい時にタイミングで呼び出してただ表示するだけになります。

バックエンド実装者のメリット

とくになし

インフラ側のメリット

サーバーのマシンリソースが節約できる

TanStack Queryはfetchしたデータをキャッシュに保存する仕組みが用意されているのでAPIのリクエストを簡単に減らせます。
リクエストが減る事で、サーバー側で処理する総量が減る事になり、マシンリソースが節約に繋がります。
具体例としては、サーバーが負荷に応じてオートスケーリングされる設定になっていればスケーリング時のコストが減りますし、API Gatewayとlambdaを使ったサーバレスな構成になっていればlambdaの実行回数が減り、インフラコスト削減に繋がります。
また、レスポンスの転送量削減に繋がるので、その分もコスト削減に繋がります。

1台の仮想サーバーで動かしているような小規模なサービスであってもサーバーの負荷軽減目的で使える気がしました。

ユーザー側のメリット

スマホのギガが節約できる。

格安プランを契約しているユーザーはデータ通信容量を気にするのかなあと思います。
主にターゲットは子供です。子供は親から与えられたスマホを渡されるので、通信プランの選択権がないのかなと思います。
自宅で使われるようなサービスならWi-Fi経由になるので通信量は気にしなくて良いと思いますが、室外で使われる可能性が高いサービスはキャッシュ機能を最大限に活用して通信料を減らしてあげられるのかなと思いました。

UX向上

リクエストが減る事で画面が表示されるまでの時間が短縮されます。
ユーザーはアプリを通じて最速で目的を達成したいと考えるので、その達成するまでの時間を少しでも減らせるという事は、お客さんの時間を奪わない事につながるため、UX向上に繋がります。

おわりに

キャッシュの管理だけは気をつける必要はありますが、色んなユースケースで使えるのでは?と思いました~

GitHubで編集を提案

Discussion