0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Reactのデータフェッチライブラリ「SWR」と「TanStack Query」の違いを比較してみた

0
Posted at

Reactのデータフェッチライブラリ「SWR」と「TanStack Query」の違いを比較してみた

ReactでAPIからデータを取得する際、便利なライブラリとして「SWR」と「TanStack Query(旧React Query)」があります。

どちらもAPI通信やキャッシュ管理を簡単に実装できるライブラリですが、具体的に何が違うのでしょうか?

今回は、それぞれの基本的な使い方から、キャッシュの仕組み、データの再取得、設定値の違いまで、コードを交えて比較していきます。

1. SWRとTanStack Queryとは?

SWR

SWRはVercelが開発しているReact向けのデータフェッチライブラリです。

シンプルなAPIでデータ取得やキャッシュ、再検証などを実装できることが特徴です。

npm install swr

TanStack Query(旧React Query)

TanStack Queryは、非同期データの取得やキャッシュ、更新などを管理するためのライブラリです。

旧React Queryという名前で知られており、現在はReact以外のフレームワークにも対応しています。

npm install @tanstack/react-query

2. なぜこれらのライブラリが必要なのか?

例えば、Reactからユーザー一覧を取得する場合、通常は次のように実装できます。

import { useState, useEffect } from "react";

function UserList() {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch("/api/users")
      .then((res) => res.json())
      .then((data) => setUsers(data))
      .finally(() => setLoading(false));
  }, []);

  if (loading) return <p>読み込み中...</p>;

  return (
    <ul>
      {users.map((user) => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

この方法でもAPIからデータを取得できます。

しかし、実際のアプリケーションでは次のような課題が出てきます。

  • 同じAPIを複数のコンポーネントから呼び出してしまう
  • 一度取得したデータを別の画面でも再利用したい
  • データが古くなったら自動的に再取得したい
  • API通信中の状態やエラーを管理したい
  • データを更新したときに、関連する画面にも反映したい

こうした処理を効率的に管理できるのがSWRとTanStack Queryです。

3. データ取得の書き方を比較する(GET)

今回は、GET /api/usersからユーザー一覧を取得する例で比較します。

SWRの場合

import useSWR from "swr";

const fetcher = (url) =>
  fetch(url).then((res) => res.json());

function UserList() {
  const { data, error, isLoading } = useSWR(
    "/api/users",
    fetcher
  );

  if (isLoading) return <p>読み込み中...</p>;
  if (error) return <p>エラーが発生しました</p>;

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

useSWRの第1引数にキャッシュを識別するキー、第2引数にデータを取得する関数を指定します。

TanStack Queryの場合

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

const fetchUsers = async () => {
  const res = await fetch("/api/users");

  if (!res.ok) {
    throw new Error("取得に失敗しました");
  }

  return res.json();
};

function UserList() {
  const {
    data,
    error,
    isPending
  } = useQuery({
    queryKey: ["users"],
    queryFn: fetchUsers,
  });

  if (isPending) return <p>読み込み中...</p>;
  if (error) return <p>エラーが発生しました</p>;

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

TanStack Queryでは、useQueryにオブジェクト形式で設定を渡します。

queryKeyがキャッシュを識別するキー、queryFnがデータを取得する関数です。

書き方の違い

比較項目 SWR TanStack Query
フック useSWR useQuery
キャッシュ識別子 Key queryKey
データ取得関数 fetcher queryFn
読み込み状態 isLoading isPending
データ data data
エラー error error

どちらも基本的なデータ取得の書き方は似ています。

TanStack QueryではqueryKeyを配列形式で指定することが特徴です。

例えば、ユーザーIDごとにデータをキャッシュしたい場合は、次のように指定できます。

useQuery({
  queryKey: ["users", userId],
  queryFn: () => fetchUser(userId),
});

これにより、ユーザーIDごとに異なるキャッシュとして管理できます。

4. キャッシュの有効期間の違い

SWRとTanStack Queryには、キャッシュやAPI通信を制御するための設定があります。

その中でも、よく比較されるのが次の2つです。

  • SWR:dedupingInterval
  • TanStack Query:staleTime

一見すると似た設定ですが、実際には役割が異なります。

SWR:dedupingInterval

dedupingIntervalは、同じキーに対するリクエストの重複を一定時間抑制するための設定です。

例えば、30秒に設定した場合です。

const { data } = useSWR(
  "/api/users",
  fetcher,
  {
    dedupingInterval: 30000,
  }
);

同じキーに対して短時間に複数のリクエストが発生した際、重複した通信を抑制します。

時刻 動作
0秒 APIリクエストを実行
10秒 同じキーのリクエストを抑制
20秒 同じキーのリクエストを抑制
35秒 重複抑制期間が終了しているため再リクエストが可能

重要なのは、30秒間データが新鮮であることを保証しているわけではないという点です。

あくまで、同じキーに対するリクエストの重複を抑えるための設定です。

TanStack Query:staleTime

一方、TanStack QueryのstaleTimeは、取得したデータをどのくらいの期間「新鮮(Fresh)」とみなすかを設定するものです。

const { data } = useQuery({
  queryKey: ["users"],
  queryFn: fetchUsers,
  staleTime: 30000,
});

この場合、データ取得後30秒間はFreshとして扱われます。

30秒を超えるとStale(古い)状態になります。

時刻 データの状態
0秒 Fresh
10秒 Fresh
20秒 Fresh
35秒 Stale

ここで注意したいのは、30秒経過した瞬間に自動でAPI通信が発生するわけではないということです。

Staleになったデータは、再取得の対象になります。

例えば、次のようなタイミングで再取得が発生します。

  • コンポーネントがマウントされたとき
  • ブラウザのウィンドウにフォーカスが戻ったとき
  • ネットワークが再接続されたとき
  • invalidateQueriesで明示的に無効化したとき

dedupingIntervalとstaleTimeは結局同じなのか?

ここは少し分かりにくい部分です。

どちらも設定した時間内に不要なAPI通信を抑制するような動作になるため、結果だけを見ると似ています。

しかし、内部で判断している内容が異なります。

  • dedupingInterval:同じキーのリクエストが短時間に重複していないか
  • staleTime:キャッシュされたデータが新鮮な状態か

つまり、前者はリクエストの重複抑制、後者はキャッシュデータの鮮度管理を目的としています。

両者とも設定時間が経過しただけで必ずAPI通信が発生するわけではありません。

また、SWRにはrevalidateOnFocusなどの再検証設定もあるため、dedupingIntervalとstaleTimeを完全に同じ機能として比較することはできません。

5. TanStack QueryにはgcTimeもある

TanStack Queryには、staleTimeとは別にgcTimeという設定があります。

const { data } = useQuery({
  queryKey: ["users"],
  queryFn: fetchUsers,
  staleTime: 30000,
  gcTime: 300000,
});

それぞれの役割は次のとおりです。

設定 役割 設定例
staleTime データを新鮮とみなす期間 30秒
gcTime 未使用になったキャッシュを保持する期間 5分

例えば、ユーザー一覧を取得した後、別の画面に移動したとします。

その結果、ユーザー一覧のQueryを利用するコンポーネントがなくなると、キャッシュは非アクティブになります。

この状態から5分間利用されなければ、キャッシュが削除されます。

つまり、次のような違いがあります。

  • staleTime:データが新鮮かどうか
  • gcTime:使われなくなったデータをいつ削除するか

この2つは別々に管理されています。

6. データ更新の書き方を比較する(PUT)

次に、ユーザー情報を更新する場合を比較します。

SWRの場合

SWRではmutateを使って、キャッシュの再検証を行うことができます。

import { useSWRConfig } from "swr";

function UserUpdate() {
  const { mutate } = useSWRConfig();

  const updateUser = async () => {
    await fetch("/api/users/1", {
      method: "PUT",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        name: "Taro",
      }),
    });

    // ユーザー一覧のキャッシュを再検証
    await mutate("/api/users");
  };

  return (
    <button onClick={updateUser}>
      更新
    </button>
  );
}

mutateを使うことで、指定したキーのキャッシュを再検証できます。

TanStack Queryの場合

TanStack Queryでは、useMutationを使ってデータ更新を管理できます。

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

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

  const mutation = useMutation({
    mutationFn: async () => {
      const res = await fetch("/api/users/1", {
        method: "PUT",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          name: "Taro",
        }),
      });

      if (!res.ok) {
        throw new Error("更新に失敗しました");
      }

      return res.json();
    },

    onSuccess: () => {
      // ユーザー一覧のキャッシュを無効化
      queryClient.invalidateQueries({
        queryKey: ["users"],
      });
    },
  });

  return (
    <button onClick={() => mutation.mutate()}>
      更新
    </button>
  );
}

TanStack Queryでは、useMutationによって更新処理を管理できます。

また、onSuccessを利用することで、更新成功後に関連するキャッシュを無効化できます。

データ更新の比較

比較項目 SWR TanStack Query
データ更新 fetchなど useMutation
キャッシュ操作 mutate QueryClient
キャッシュの再検証 mutate invalidateQueries
更新中の状態 useSWRMutationなど isPending
成功・失敗時の処理 コールバックなど onSuccess / onError

なお、SWRにもuseSWRMutationという更新処理向けのフックが用意されています。

そのため、SWRではデータ更新ができないというわけではありません。

どちらもデータ更新に対応していますが、APIの設計やキャッシュ管理の方法に違いがあります。

7. まとめ

SWRとTanStack Queryは、どちらもReactアプリケーションにおけるサーバー状態の管理を効率化するライブラリです。

基本的なデータ取得やキャッシュ、データ更新などは両方で実現できます。

比較項目 SWR TanStack Query
データ取得 useSWR useQuery
データ更新 useSWRMutationなど useMutation
キャッシュ識別 Key queryKey
キャッシュ再検証 mutate invalidateQueries
リクエスト重複抑制 dedupingInterval Queryの共有・重複排除
データの鮮度管理 再検証の仕組み staleTime
キャッシュの保持・削除 キャッシュ管理 gcTime

SWRの特徴

  • シンプルなAPIでデータ取得を実装できる
  • キャッシュや再検証の仕組みが分かりやすい
  • 小〜中規模のデータ取得処理にも取り入れやすい

TanStack Queryの特徴

  • データ取得と更新の役割が明確
  • staleTimeやgcTimeなど、キャッシュを細かく管理できる
  • Mutationやキャッシュの無効化など、複雑なデータ管理にも対応しやすい

どちらが優れているというわけではなく、アプリケーションの規模やデータ管理の複雑さに応じて選択することが重要です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?