TanStack Queryを使う

2024-10-04

react
Example Image

Next.jsにTanStack Queryを入れたら、Google Mapの表示が爆速になりました!

テンションがあがったので手順をざっくりまとめます。

TanStack Query:非同期状態を効率よく管理できるライブラリ

Advanced Server Rendering | TanStack Que...

Welcome to the Advanced Server Rendering...

favicon tanstack.com

OGP image

ライブラリをインストールした後、Provider.tsxを作成します。

'use client';
import {
  isServer,
  QueryClient,
  QueryClientProvider,
} from '@tanstack/react-query';

function makeQueryClient() {
  return new QueryClient({
    defaultOptions: {
      queries: {
        staleTime: 60 * 1000,
      },
    },
  });
}

let browserQueryClient: QueryClient | undefined = undefined;

function getQueryClient() {
  if (isServer) {
    return makeQueryClient();
  } else {
    if (!browserQueryClient) browserQueryClient = makeQueryClient();
    return browserQueryClient;
  }
}

export default function Provider({ children }: { children: React.ReactNode }) {
  const queryClient = getQueryClient();
  return (
    <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
  );
}

上記のコンポーネントを、Layout.tsxの子要素をラップするように設置します。

これで、アプリケーション全体で QueryClient を使用できるようになります。

<Provider>{children}</Provider>

Page.tsxで下記のようにデータをプリフェッチします。

import {
  dehydrate,
  HydrationBoundary,
  QueryClient,
} from '@tanstack/react-query';
import GoogleMap from './components/GoogleMap';
import { fetchMarkers } from './marker';

export default async function StaticPage() {
  const queryClient = new QueryClient();
  await queryClient.prefetchQuery({
    queryKey: ['fetchMarkers'],
    queryFn: fetchMarkers,
    staleTime: 5 * 60 * 1000,
  });
  const dehydratedState = dehydrate(queryClient);

  return (
    <div className="grid items-center justify-items-center min-h-screen">
      <HydrationBoundary state={dehydratedState}>
        <GoogleMap />
      </HydrationBoundary>
    </div>
  );
}

クライアントコンポーネントでuseQueryを使って、プリフェッチしたデータにアクセスします。

'use client';

import React from 'react';
import { APIProvider, Map, AdvancedMarker } from '@vis.gl/react-google-maps';
import { useQuery } from '@tanstack/react-query';
import { useRouter } from 'next/navigation';
import { fetchMarkers } from '../marker';
import { Marker } from '@prisma/client';

const GoogleMap = () => {
  const router = useRouter();
  const {
    data: markers,
    isLoading,
    isError,
    error,
  } = useQuery<Marker[], Error>({
    queryKey: ['fetchMarkers'],
    queryFn: fetchMarkers,
  });

これで、キャッシュを利用して効率よくデータを表示できるようになりました!

プリフェッチしたデータに対しCRUD処理を行うときは、キャッシュをクリアする処理を加えます。

クリアするのを忘れると、データを削除したのに画面の表示が変わらない・・・というバグが発生します。

キャッシュのクリアは、削除ボタンだとこんな感じです。

const deleteMarkerMutation = useMutation({
    mutationFn: () => deleteMarker(id),
    onSuccess: () => {
      queryClient.invalidateQueries({
        queryKey: ['fetchMarkers'],
      });
      alert('施設をリストから削除しました');
      router.push('/lists');
    },
  });

invalidateQueriesのあたりで、'fetchMarkers'のQueryKeyがついたデータのキャッシュをクリアしています。

share