Nextjs+TanstackQuery+SSRの話

  • #開発
  • #nextjs
  • #tanstack
  • #SSR

こんにちは。CURUCURUエンジニアの長尾です。 今回はNextjs + tanstack query + SSRをテーマにお話します。


概要

今回既存ページの改善を行うため、SSRでのレンダリングを目指して実装を進めていましたが、 いくつか躓いたポイントがあったのでそのお話をします。

NextjsでSSRするには?

弊社ではフロントエンドでNextjsを採用しています。 現在はバージョンは12系です。

NextjsにおいてSSRするにはどうしたらいいかは知ってる人は多いと思います。 そう、getServerSidePropsを実装すればよいですね。

以下、Nextjs公式から抜粋したものです。皆さんのイメージ通りですよね。

export async function getServerSideProps() {
  const res = await fetch(`https://.../data`)
  const data = await res.json()

  return { props: { data } }
}
 
export default function Page({ data }) {
  return <div>Hello SSR world</div>
}

TanstackでSSRするには?

これが今日のお話の答えになってしまうのですが、Hydrateを使うことです。

Hydrateはtanstackが提供しているコンポーネントですが、それを利用することでサーバサイドでプリフェッチを行いDehydrateし、クライアントでのHydrateを可能にしています。

使い方は簡単です。まずは下準備。

// 下準備として_app.tsxにHydrateコンポーネントを追加する
import {
  Hydrate,
  QueryClient,
  QueryClientProvider,
} from '@tanstack/react-query'

function MyApp({ Component, pageProps }: AppPropsWithLayout) {
  const [queryClient] = React.useState(() => new QueryClient())
  const { dehydratedState } = pageProps as { dehydratedState: unknown; };

  return (
    <QueryClientProvider client={queryClient}>
      <Hydrate state={dehydratedState}>
        <Component {...pageProps} />
      </Hydrate>
    </QueryClientProvider>
  )
}

次に使うページのgetServerSidePropsにて

export const getServerSideProps: GetServerSideProps = async (ctx: GetServerSidePropsContext) => {
    ...
    const queryClient = new QueryClient();
    await queryClient.prefetchQuery(['get-data'], fetcherGetData);
	...
}

あとはコンポーネント側で以下のように使ってあげればOKです。

const useGetData = () => {
    return useQuery({
        queryKey: ['get-data'],
        queryFn: fetcherGetData,
        suspense: true,
    });
};

export const ShowDataBlock = () => {
	return <ErrorBoundary fallback={<></>}>
		<Suspense fallback={<></>}>
			<SearchBoxBrand />
		</Suspense>
	</ErrorBoundary>;
};

const ShowData = () => {
	const data = useGetData();
	return <div>
		{data.map(d => <p>d.name</p>)}
	</div>
};

getServerSidePropsでプリフェッチしたものがqueryKeyをもとにクライアントでも使えるようになって初期表示が行われるという感じでしょうか。

Hydrateに辿り着くまで

当初、getServerSidePropsでaxiosを使ってデータ取得を行っていましたが、 将来的にnextjs13に移行することを考えてgetServerSidePropsをファットにはしたくありませんでした。

そこで、tanstackを使ってコンポーネント側でのデータフェッチを行うこと。 しかしコンポーネント側でのデータフェッチを行うと以下のようなエラーに遭遇しました。

Error: This Suspense boundary received an update before it finished hydrating. This caused the boundary to switch to client rendering. The usual way to fix this is to wrap the original update in startTransition.

上記の解決を行うために色々と調べていたんですが、そこでTanstackの公式にSSRの実装方法の記載があったので試したところ上記エラーもでなくなりました。

Hydrate, Dehydrate

Hydrateという単語はフロントエンドを触る方なら耳にしたことあると思います。 Hydrateは和訳だと水分補給するという意味です。 つまり、初回レンダリング後に渡されたjsを組み込むことでhtmlに水分補給しているんですね。 逆に今回出てきたDehydrateは水分を取り除くとか脱水という意味があります。

言葉の意味を知ると事象をイメージ化して理解しやすいですよね。

まとめ

今回はNextjs + tanstack query + SSRについてお話しました。

以上、長尾がお届けしました。

メンバー募集

CURUCURUでは開発メンバーを募集中です。 CURUCURUの開発に興味があったり、モダンな開発環境で挑戦してみたいという方がいましたら、ぜひこちらも覗いてみてください!