📚

Next13でasyncを使った非同期サーバーコンポーネントを作ると型エラーが発生する問題について

2023/02/18に公開

はじめに

Next13で搭載されたReact RFCでは、基本的に全てのコンポーネントをSSRで描画するものとし、SSRで描画するコンポーネントでasync functionを使用することができるようになりました.
これにより、従来のServerSideProps()といった方法を使わずに、直接関数コンポーネント内でawaitSuspenseを使用した非同期処理が可能になりました.

しかしながら、この作成したasync functionをpage内で呼び出すとJSX elementとして認識されず次のようなエラーが発生します.

'NewPostGrid' を JSX コンポーネントとして使用することはできません。
  その戻り値の型 'Promise<Element>' は、有効な JSX 要素ではありません。
    型 'Promise<Element>' には 型 'ReactElement<any, any>' からの次のプロパティがありません: type, props, key
Type error: 'NewPostGrid' cannot be used as a JSX component.
  Its return type 'Promise<Element>' is not a valid JSX element.
    Type 'Promise<Element>' is missing the following properties from type 'ReactElement<any, any>': type, props, key

yarn devではなんだかんだ動きますが、yarn buildだとちゃんと怒られるはずです.
自分自身まだNext13への理解が浅いので全然明後日の方向のことをやっている可能性もありますが、一応解決したので書いておきます.

env

  • MacBook Pro Apple M1 16GB ( MacOS 13.1 )
  • typescript: "4.9.4"
  • next: "13.1.2"
  • react: "18.2.0"

非同期コンポーネント

次に、microCMSから最新の4記事を取得し、タイル状に配置するコンポーネントを示します.
Next Linkの使い方の件に関しては、正直これも新機能でどう使うのが正解なのかいまいち掴みきれていないところがあるので苦肉の策ということでお許しください.意図を汲んで解決策を教えてくれる方はぜひ助けていただけると嬉しいです.

NewPostGrid.tsx
import PostTile from '@/components/PostTile'
import { getList } from '@/libs/microcms'
import Link from 'next/link'
import dayjs from 'dayjs'

export default async function NewPostGrid() {
  const { contents } = await getList({
    limit: 4,
    orders: '-createdAt',
  })

  if (!contents || contents.length === 0) {
    return <h1>No contents</h1>
  }

  return (
    <>
      <div className='grid sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 md:gap-6 xl:gap-8 mt-10'>
        {contents.map((post) => {
          return (
            <div key={post.id}>
              <Link href={`/blog/${post.id}`}>
                <PostTile
                  title={post.title}
                  date={dayjs(post.createdAt).format('YYYY年MM月DD日')}
                  image={post.eyecatch?.url ?? ''}
                  tag={post.category.name}
                />
                <a></a>
              </Link>
            </div>
          )
        })}
      </div>
    </>
  )
}

呼び出し(ページコンポーネント)

先ほどのコンポーネントをページコンポーネント内で読み込んでみます.

page.tsx
import Image from 'next/image'

import Hero from '@/components/Hero'
import NewPostGrid from '@/components/NewPostGrid'
import ScrollRevealContainer from '@/libs/ScrollRevealContainer'

export default function Home() {
  return (
    <>
      <Hero />

      <ScrollRevealContainer move='top' delay={10}>
        <NewPostGrid />
      </ScrollRevealContainer>

型の警告が出ました.
まだ情報が少なく謎深いですが、実は公式docsにめっちゃでっかく書いてありました.

https://beta.nextjs.org/docs/data-fetching/fetching#asyncawait-in-server-components

Warning: You can use async/await in layouts and pages, which are Server Components. Using async/await inside other components, with TypeScript, can cause errors from the response type from JSX. We are working with the TypeScript team to resolve this upstream. As a temporary workaround, you can use {/* @ts-expect-error Server Component */} to disable type checking for the component.

まだ公式Next13 Betaの問題のようです.
指示に従い、@ts-expect-errorを用いて型チェックを切ります.
次のように変更するとあくまで一時的にですがなおせます.

page.tsx
 <ScrollRevealContainer move='top' delay={10}>
        {/* @ts-expect-error Server Component */}
        <NewPostGrid />
</ScrollRevealContainer>

おわりに

Discussion