React Query・キャッシュを使ってWebページのレスポンスを早くする

- [あるWebサービスの開発メモ・目次ページに戻る](https://qiita.com/ishi32/items/152a1ecf7eeee7a8fb57)
- [あるWebサービスの開発メモ・React Query に戻る](https://qiita.com/ishi32/items/fc0157922fd0c4fc73b6)

以下の記載は、8/28 19:00 に変更しました。昨日書いた記事では「mui の ListItemButton コンポーネントの page link で page を呼び出すと React Query のキャッシュがうまく機能しない」という問題の影響で、意味不明の結果を報告してしまいました

"next/link" の Link コンポーネントを利用すると問題なくキャッシュが効くことがわかりましたので、以下の記載を全て改めました 

## 戦略

- トップ (Welcome) ページを見せている間に、PostgreSQL データベースよりユーザが頻繁にアクセスするデータを取得し、キャッシュしておく
- 支障がない範囲でキャッシュの staleTime を長くしておく (とりあえず 15 分で様子見しておく)
- cacheTime は 1 時間で様子見してみる

## 実装

(./pages/_app.tsx)

```
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
```

```
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      retry: false,
      refetchOnWindowFocus: false,
      // suspense: true,
    },
  },
});
```

- [公式](https://tanstack.com/query/v4/docs/quick-start) の通りに queryClient をつくっています
- React 17 なので suspense: true, はコメントアウトしています

(JSX 部分)

```
return (
  <QueryClientProvider client={queryClient}>
    <HiddenQueryData />
    <Component {...pageProps} />
    <ReactQueryDevtools initialIsOpen={false} />
  </QueryClientProvider>
);
```

- ここも公式の通りに QueryClientProvider で react-query を使いたいコンポーネントを囲っています
- HiddenQueryData で useQuery しています

(useQuery 部分)

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

```
query = useQuery({
    queryKey: ["postsnow"],
    queryFn: getPostsnow,
    staleTime: 900000,
    cacheTime: 3600000,
  });
```

- ここも公式の通りに queryKey と queryFn を指定しています
- staleTime と cacheTime は戦略通りの値にしています

(キャッシュ取得部分)

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

```
const queryClient = useQueryClient(); 
```

```
cache = queryClient.getQueryData(["postsnow"]);
```

## 動作確認

### 結果

結論から言いますと、事前の想定とはちょっと違ったのですが、申し分のない動作でした

- ``` queryKey: ["postsnow"] ``` でキャッシュしているページから別ページを表示後にキャッシュしているページに戻ると、キャッシュが効いているので即座に画面描画が完了する
- この際に、別ページ表示中にデータベース側を更新 (1データ削除) し、キャッシュしているページに戻ると、キャッシュはそのまま
- staleTime を経過すると
  - getQueryData でキャッシュにアクセスするとキャッシュが取得できる (データベースアクセスはなし)
  - useQuery を実行するとキャッシュが取得でき、その後データベースアクセスが発生してキャッシュが更新され、これも useQuery で取得できる

### 詳細

では、以下に詳細を記録しておきます

- ページA: ``` queryKey: ["postsnow"] ``` で useQuery を発行しているページ
- ページB: ``` queryKey: ["postsnow"] ``` で useQuery も getQueryData も発行しないページ
- ページC: ``` queryKey: ["postsnow"] ``` で getQueryData を発行しているページ

(18時05分に ページA で ブラウザでリロード)

![スクリーンショット 2022-08-28 18.06.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/539175/e576a938-4938-aa28-a620-0d6f325181c1.png)

- react-query-devtools 上で ["postnow"] と ["catsallplus"] が fresh になっている
- posts に対する select の preflight で 1.1s かかっている
- cats に対する select の preflight でも 1.77s かかっている
- posts に対する select の fetch で 663ms かかっている
- cats に対する select の fetch で 573ms かかっている
- posts の fetch と cats の fetch は非同期で処理できている

(ページB を表示)

![スクリーンショット 2022-08-28 18.07.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/539175/2e7f8942-7154-0f6f-7ad4-0dabba0024d4.png)

- ここでは 3 つの fetch が実行されて 合計 5 つのキャッシュになっている

18:08 ページA に戻る

![スクリーンショット 2022-08-28 18.08.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/539175/eaea9d4c-e0c7-9013-ecbc-e5f94b2c1728.png)

- 画像ファイル以外の fetch はなし
  - 画像ファイルもキャッシュが効いている
- このとき Data Explorer でキャッシュを確認すると
  - データ数: 5 items
  - 0: "react-query が大好き"
  - 1: "私は雨が好き"
  - 2: "人生ゲーム令和版"
  - 3: "私は sirokuro.site が大好き！"
  - 4: "私は Mac Book が大好き！"

(ページB を表示)

(supabase の Table editor で "人生ゲーム令和版" を論理削除)

![スクリーンショット 2022-08-28 18.12.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/539175/54c0e33b-6db2-fa5f-eeb3-55bcc342cf1e.png)

18:13 ページA に戻る

![スクリーンショット 2022-08-28 18.14.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/539175/227974b6-4aff-1850-e128-50642e51d403.png)

- このとき Data Explorer でキャッシュを確認すると変化なし

18:21 ページA

![スクリーンショット 2022-08-28 18.21.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/539175/641c33df-33b0-2ea3-00d2-c7a68642d007.png)

- 15分以上経過したが fresh のまま

ページB を表示

![スクリーンショット 2022-08-28 16.47.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/539175/c27ecb62-471e-6227-fef4-d6bcdeb2f048.png)

- stale に変わる
- データ数: 5 items のまま

ページC を表示

![スクリーンショット 2022-08-28 18.24.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/539175/757ed632-2827-8a6a-ec57-04794547ed3f.png)

- まだ stale のまま
- データ数: 5 items のまま

18:26 ページA を表示

![スクリーンショット 2022-08-28 18.26.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/539175/0dba1ea2-12d5-735d-4ff3-d538797fc096.png)

- 画面は即時で表示されたので、キャッシュから初期表示されたものと思われる
- preflight 896ms
- fetch 645md
- データ数は 4 になり "人生ゲーム令和版" がなくなっている

## まとめ

React Query の導入効果には素晴らしいものがありました。あまりリアルタイム性が重要ではないデータの場合、react-query のキャッシュを用いて画面レスポンス重視の (そして通信量を抑えた) アプリを実現できます。