tanstack query Decoder Error
概要
next13でtanstack queryを使う時は, prefetch
してくるかinitialData
をいれるかの方法かと思います。今回は存在しないデータのパスのページを見ようとしたとき、そこの内部でprefetch
を使っているので以下のエラーが出たという備忘録です
このエラーは直訳すると、サーバーは、サーバー レンダリング中のエラーが原因で、このsuspense境界を終了できませんでした。クライアントレンダリングに切り替えました。ということらしいです。
条件
- next: "13.4.8"
- @tanstack/react-query: "^4.33.0",
とします.
出現コード
//getQueryClient.ts
export const getQueryClient = cache(() => new QueryClient({
defaultOptions: {
queries: {
staleTime: 60 * 1000,
retry: false,
}
} }))
// Container.tsx
type RootContainerProps = { params: { pokemonName: string } }
export const RootContainer = async ({ params }: RootContainerProps)=>{
await pokemonService.prefetchPokemonByName(params.pokemonName)
const dehydratedState = dehydrate(getQueryClient())
return (
<>
<Hydrate state={dehydratedState}>
<SuspenseLoading>
<PokemonContainer params={params} />
</SuspenseLoading>
<SuspenseLoading>
<PokemonContainer params={params} />
</SuspenseLoading>
</Hydrate>
</>
)
}
// layout.tsx
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body className={inter.className}>
<MyErrorBoundary>
<QueryClientProviders>
{children}
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProviders>
</MyErrorBoundary>
</body>
</html>
)
}
となっています. RootContainer
にはparams
にポケモンの名前がきます.存在しないとAPIにて404のエラーが返ることになっています。しかし、prefetch
の場合は「A promise is returned that will either immediately resolve if no fetch is needed or after the query has been executed. It will not return any data or throw any errors.」の公式通り、errorはthrowされません。
サーバでthrowされないとここで囲っているSuspense
が起動し続けてサーバ レンダリングのコンポーネントがエラーを吐くという流れかなと思います。
従って、errorが放置されたままになるとクライアントに切り替わるという感じだと思います。
これを修正するために、prefetch
ではなくfetchQuery
を使います。以下のように書き換えます.
export const RootContainer = async ({ params }: RootContainerProps)=>{
try {
await pokemonService.fetchQueryPokemonByName(params.pokemonName)
} catch (error) {
if (error instanceof ResponseError) {
switch (error.getStatusCode) {
case 404:
return notFound()
default:
return <ErrorComponent statusCode={error.getStatusCode} />
}
} else {
return <ErrorComponent />
}
}
const dehydratedState = dehydrate(getQueryClient())
return (
<>
<Hydrate state={dehydratedState}>
<SuspenseLoading>
<PokemonContainer params={params} />
</SuspenseLoading>
<SuspenseLoading>
<PokemonContainer params={{ pokemonName: 'pikachu' }} />
</SuspenseLoading>
</Hydrate>
</>
)
}
最初にAPI取得時にエラーになるときはnotFound
を渡すことで404のエラーを返すことができます。他のエラーの場合はErrorComponent
として包んで返してあげましょう.
結論
errorの放置はしないようにした方が良い
Discussion