LoginSignup
2
4

More than 1 year has passed since last update.

React Queryを使ったGlobal Server State マネジメント

Last updated at Posted at 2022-09-29

v2-055fd5bd248e4f83efd02778b9773254_1440w.jpeg

前書き

React Queryがv4になったタイミングで、正式な名称はTanStack Queryになりましたが、この記事内ではReact Queryと呼ばさせていただきます。
公式ドキュメント:

どんなwebシステムでもフロントからサーバーへのデータの取得、修正削除などのリクエストは必ず発生します。
React Query使用することで、何がどう便利になったのか、簡単なサンプルを見て、体感しましょう。:point_up_tone1:
サンプル少し長いので最後まで付き合っていただけると幸いです。:relaxed:

サンプル

簡単なリストデータを取得することを想定しているサンプルになります。

  • axiosで書いてますが、fetchでも使い方同じです。
async function requestList() {
  const { data } = await axios.get("/api/list");
  return data;
}

4行のコードを書くだけで、サーバーからデータの取得はできます。:point_up_tone1:

次はこのリクエストをフックにして、使い勝手を改善します。

function useListQuery() {
  const [data, setData] = React.useState([]);

  React.useEffect(() => {
    (async () => {
        const { data } = await axios.get("/api/list");

        setData(data);
    })();
  }, []);

  return {
    data,
  };
}

function List() {
  const { data } = useListQuery();
  
  return (
    <div>
     {data.map((item) => {
         return <div>{item}</div>
      })}
    </div>
  );
}

ここまで書いたら、一見良さげですが:relaxed:
リクエスト送った後のエラー、ローディング処理はまだのため、追加しておきます。:point_up_tone1:

function useListQuery() {
  const [data, setData] = React.useState([]);
  const [isLoading, setLoading] = React.useState(false);
  const [error, setError] = React.useState();

  React.useEffect(() => {
    (async () => {
        setLoading(true);
        
        try {
          const { data } = await axios.get("/api/list");
          setData(data);       
        } catch(e) {
          setError(e);
        }
        setLoading(false); 
    })();
  }, []);

  return {
    data,
    isLoading,
    error,
  };
}

以上、これで良くあるリクエストを管理するフックが完成しました、それを実現するために25行のコードも書いてしまったのですが、

React Query使う場合、どうなるでしょう。

React Queryサンプル

React Queryを利用するためにはreact-queryパッケージをインストールする必要があります。

yarn add react-query
or
npm install react-query

先のフックをReact Query使って書き直します。

import { useQuery } from 'react-query';
...

function useListQuery() {
  const {data, isLoading, isError} = useQuery("list", async () => {
    const { data } = await axios.get("/api/list");
    return data;
  });

  return {
    data,
    isLoading,
    isError,
  };
}

これだけで完了です、かなりシンプルになったと思います:point_up_tone1:
React Queryではコードにある通り、第一引数に任意の名前のユニークキー、第二引数にはpromiseを戻す関数を設定します。

const result = useQuery('ユニークキー', Promiseを返す関数)

React Queryキャッシュ機能はこのユニークキーによって実現している、一度取得したデータはしばらキャッシュされて、キーが変わるタイミングで、データを再度取得します。

ユニークキーは文字列以外、リストやオブジェクトにすることもできます。

キーサンプル
useQuery('list', ...)
useQuery(['list'], ...)

useQuery(['list', 1], ...)
userQuery(['list', {
  page: 1
}])
useQuery({
  type: 'list',
  page: 1
})

それらを踏まえて、ユーザーごとのデータを取得する際に、ユニークキーの書き方は下記のコードのようになるのが適切です。

ユーザーデーター取得サンプル
 const { data }  = useQuery(
    ['getuser', userID],
    () => axios.get(`/api/getuser/${userID}`).then(res => res.data);
)

さらに、useQueryの第三の引数に{ enabled: userID !== undefined }を追加すれば、userID渡さない限り、リクエストは送信しません。

ユーザーデーター取得サンプル
 const { data }  = useQuery(
    ['getuser', userID],
    () => axios.get(`/api/getuser/${userID}`).then(res => res.data),
    { enabled: userID !== undefined }
)

データを変更する場合useMutationというフックがあります。

import { useMutation } from "react-query"

function App() {
  const [isLoading, isError, mutate] = useMutation(() => axios.post("/api/list", { newList }));

  async function onSubmit() {
    await mutate()
  } 
  if (isLoading) {
     return <p>データ送信中...</p>;
  }
  if (isError) {
     return <p>エラー...</p>;
  }
  return <button onClick={onSubmit}>submit</button>;
}

その他の使い方は公式ドキュメント参考にしてください。

キャッシュコントロール

staleTimeとcacheTime

React Queryは取得してきたデータを自動的にキャッシュする際に、条件を設定することができます。

  • cacheTime: データをキャッシュする時間, デフォルトは5分。
  • staleTime: キャッシュしたデータが古くなったとみなす時間,デフォルトは0。

例えばlistデータを取得し、その1分後再度listを取得し直す場合、すでにlistはキャッシュされているのでリクエストなしで表示可能です。
一方でstaleTimeが0のため、キャッシュを返した後にサーバーにリクエストを投げます。
データが新しくなっている場合はそちらの値に書き換えられます。

サンプル
const { isLoading, isError, data } = useQuery(
  "list",
  async () => {
   const { data } = await axios.get("/api/list");
   return data;
 },
  { staleTime: 0, cacheTime: 5 * 60 * 1000 }
)

リクエストの送信を最小限に抑えたい場合、staleTimeInfinity に設定することができます。
そうすれば、キャッシュは常に新鮮なものとみなされるのでバックグラウンドでのフェッチは自動的には行われません。

:point_up_tone1:でも明らかデータ変更があった場合、最新のデータを表示したいときにuseQueryClientを使うことができます。

useQueryClient

useMutation使ってデータを変更した後に、React Queryにキャッシュされてる古いデータを更新したい場合、
useQueryClient使って、手動でキャッシュの更新ができます。

仮に、useQueryを使って管理してるデータの中に、ユニックキーがlistのものがあるとします、useMutationでそのデータに新たなデータを追加した際に、追加が成功すれば queryClientにキーを指定してデータを更新できます。

useQueryClientサンプル
import {useQueryClient} from 'react-query'

const listForm = (data) => {
 ...
 const queryClient = useQueryClient()
 const { mutate } = useMutation(
   () => axios.post("/api/list", { newList }),
   { onSuccess: () => {
       queryClient.invalidateQueries(["list"])
     }
   }
 )

これで更新されたlistが表示されるようになると思います。

最後に

この記事はあくまでReact Query使用する場合の雰囲気を伝わるもので、実際プロジェクトに導入する際に、ルートコンポネートにQueryClientProviderを追加するところから始めると思います、詳細は公式ドキュメントをご参考ください。

2
4
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
2
4