はじめに
Next.js 13とTanStack Query(React Query)を使ってハマってしまった部分があったので、備忘録として投稿します。
現在(2023/08)は、日本語の記事がまだ少なかったので、情報収集してまとめてみました。
参考になれば嬉しいです!
何でハマったか
具体的には、useQuery
の使用でハマりました。
原因
useQueryはClient Componentsでしか使用できませんが、Next.js 13はデフォルトではServer Compenentsになっています。
そのため、useQueryを使用するには、別途設定する必要があります。
通常は下記のようにQueryClient
インスタンスを生成し、QueryClientProvider
に生成したインスタンスを設定し、QueryClientProvider
で親コンポーネントをラップするだけでuseQuery
は使用可能ですが、Next.js 13では上手くいきませんでした。
import './globals.css'
import type { Metadata } from 'next'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
export const metadata: Metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
}
const client = new QueryClient();
const RootLayout: React.FC<{ children: React.ReactNode }> = ({
children
}) => {
return (
<html lang="en">
<body>
<QueryClientProvider client={client}>
{children}
</QueryClientProvider>
</body>
</html>
)
}
export default RootLayout;
解決策
こちらのTanStack Queryのドキュメントに載っていました。
Providers
を作成し、親コンポーネントをProviders
でラップするようにします。
Providers作成に必要なライブラリをインストール
プロジェクトのルートディレクトリに移動し、下記を実行します。
npm install @tanstack/react-query-next-experimental
Providersの作成
ReactQueryStreamedHydration
でchildlen
をラップし、QueryClient
を設定したQueryClientProvider
でReactQueryStreamedHydration
をラップします。
'use client'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import * as React from 'react'
import { ReactQueryStreamedHydration } from '@tanstack/react-query-next-experimental'
export function Providers(props: { children: React.ReactNode }) {
const [queryClient] = React.useState(() => new QueryClient())
return (
<QueryClientProvider client={queryClient}>
<ReactQueryStreamedHydration>
{props.children}
</ReactQueryStreamedHydration>
</QueryClientProvider>
)
}
親コンポーネントにProvidersを設定
プロジェクト全体でuseQuery
を使用する場合は、ルート(Next.js 13ではlayout.tsx)にProviders
を設定します。
先ほど作成したProviders
をimportしてchildren
をラップするだけでOKです。
import './globals.css'
import type { Metadata } from 'next'
import Providers from '@/providers'
export const metadata: Metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
}
const RootLayout: React.FC<{ children: React.ReactNode }> = ({
children
}) => {
return (
<html lang="en">
<body>
<Providers>
{children}
</Providers>
</body>
</html>
)
}
export default RootLayout;
useQueryを使用して動作確認
これでやっとuseQuery
を使用できるようになりました!
useQuery
を使用する際は、使用するコンポーネントをClient Componentsとして扱う必要があるため、ファイルの先頭に"use client;"
を付けるのを忘れずに!
"use client";
import { useQuery } from "@tanstack/react-query";
import PostResponseType from "@/types/postResponseType";
import axios from "../lib/axios";
import Loading from "@/components/Loading";
export const Page: React.FC = () => {
const getData = async (): Promise<PostResponseType[]> => {
const res = await axios.get("/posts");
return res.data ?? [];
}
const { data, isLoading, isError } = useQuery<PostResponseType[]>({
queryKey: ["posts"],
queryFn: getData
});
return (
<div className="p-4">
<div className="pb-8 text-5xl text-center">posts</div>
<div className="pb-8 w-3/5 m-auto">
{isLoading ? (
<Loading
text="データ取得中です。しばらくお待ちください。"
/>
) : isError ? (
<p>Error!</p>
) : (
data?.map((post, index) => (
<ul key={index} className="pb-8 text-left">
<li>
<strong>userId: </strong>
<span>{post.userId}</span>
</li>
<li>
<strong>id: </strong>
<span>{post.id}</span>
</li>
<li>
<strong>title: </strong>
<span>{post.title}</span>
</li>
<li>
<strong>body: </strong>
<span>{post.body}</span>
</li>
</ul>
))
)}
</div>
</div>
)
}
export default Page;
Unhandled Runtime Error
Error: Attempted to call useQuery() from the server but useQuery is on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component.
おわりに
日本語で情報がなくても、英語で検索することで解決できる場合もあります!
英語ができなくても、DeepLなど優秀な翻訳アプリがあるので、是非挑戦してみてください!