TaikiTkwkbysh
@TaikiTkwkbysh (WAKA Engineer)

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

【Next.js 13.4】 ビルドエラーの理由がわかりません。

解決したいこと

Next.jsでビルドした際、以下のようなエラーが発生します。
解消はされたのですが、解消された理由が分かりません。

ご存じの方いらっしゃいましたら、ご教示のほど宜しくお願い致します。

発生している問題・エラー

src/app/about/page.tsx
Type error: Page "src/app/about/page.tsx" does not match the required types of a Next.js Page.
  "AboutPage" is not a valid Page export field.
Error: Command "npm run build" exited with 1

Next.jsのバージョンは13.4.18
その他のバージョンおよびディレクトリ構造、ソースは以下の通りです。

・package.json

{
  "dependencies": {
    "@headlessui/react": "^1.7.17",
    "@heroicons/react": "^2.0.18",
    "@types/node": "20.5.0",
    "@types/react": "18.2.20",
    "@types/react-dom": "18.2.7",
    "autoprefixer": "10.4.15",
    "date-fns": "^2.30.0",
    "eslint": "8.47.0",
    "eslint-config-next": "13.4.18",
    "microcms-js-sdk": "^2.5.0",
    "next": "13.4.18",
    "postcss": "8.4.28",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "tailwindcss": "3.3.3",
    "typescript": "5.1.6"
  }
}

・ディレクトリ構造

root
  |
 └ src
    └ app
       ├ about
       |   └ page.tsx
       |
       ├ portfolio
       |   └ page.tsx
       |
       ├ layout.tsx
       └ page.tsx

・app/about/page.tsx

const AboutPage = () => {
  return <p>About</p>;
}
export default AboutPage;

自分で試したこと

app/about/page.tsxのソースを以下のように修正したところ、なぜかビルドが通りました。

・app/about/page.tsx

export default function AboutPage() {
  return <p>About</p>;
}

なぜexport default functionにしたら通ったのでしょうか。
ご教示のほど宜しくお願い致します。

2

1Answer

注意
できる限り正確な情報を提供できるよう注意していますが、間違っていたり誤解を招く可能性があります。
正確な情報は、Next.jsドキュメントサバイバルTypeScriptなどの信頼できる一次情報源から得ることをお勧めします。

こんにちは。nabeken5です。

解決策

該当するエラーについてのドキュメントには、

ファイルがページであることを意図している場合は、React Component で export ではなく export default を使用していることを再確認してください。すでに export default を使用している場合は、返される値が有効な React Component であることを確認してください。

Next.jsドキュメント(英語版)より。DeepLで和訳

と記載されています。
アロー関数に関する記述はないですが、 export default の利用で問題ないです。

エラーの原因

注意
情報量が多かったeslint-plugin-reactについての情報を一部利用しています。

Reactコンポーネントに名前がついていないのが原因です。

アロー関数は、従来の関数(関数宣言関数式)と比べ、関数自体に名前を付けないのが特徴です。

// 従来の関数(関数式)
[1, 2, 3].map(function (n) {
  return n + 1;
});
// アロー関数
[1, 2, 3].map((n) => n + 1);

サバイバルTypeScriptより。

アロー関数を定義(変数名をつけること)した場合でも、アロー関数自体は関数名を持ちません。

そのため、デバッグが難しい(関数名が確定しない)という問題があり、Reactコンポーネントでの定義時にエラーを出すようになったらしいです。

アロー関数として書くと(特に私が提供した例では)、信頼できる関数名が使えなくなり、デバッグに支障をきたします。コンポーネントは決してアロー関数であってはなりません。

Reactでは、アロー関数とそうでない関数の違いは他にありません。

jsx-eslint/eslint-plugin-react Issueでの@ljharbさん(jsx-eslint開発メンバー)の発言。DeepLで和訳


以上を踏まえ、PageやComponentの宣言ではアロー関数ではなくexport defaultを利用することをお勧めします。
Next.jsドキュメントでもexport defaultになっています)

蛇足ですが、@ljharbさんの発言(Reactでは、アロー関数とそうでない関数の違いは他にありません。)の通り、通常の宣言時ではアロー関数と従来の関数の使い分けは問題ではないとされています。

Next.jsとReactでは、それは事実上まったく問題ではありません。

arrow関数と古い関数の主な違いは、古い関数にはthisオブジェクトがバインドされていることです。thisオブジェクトには、最近の開発者にはあまりなじみのない仕組みがあり、React開発ではあまり使われませんが、たとえばFastifyのようなバックエンドフレームワークでは、ときどきthisオブジェクトを使います。

また、hoistingもあります。これは、定義される前に何かを使用できることを意味しますが、私たちはモジュールで作業するので、これが問題になることはほとんどありません。

vercel/next.js Discussionでの@icyJosephさんの発言。DeepLで和訳

そのため、handlerなどを定義するときにアロー関数を使う分には全く問題はありません。

コードが見やすくなり、function内にも関数を定義できるため、短くシンプルな記述が可能になります。

通常のTypeScriptでのアロー関数と従来の関数の違いや使い分けについては、 サバイバルTypeScriptを参考にしてください。

参考

  • Next.jsドキュメント

  • jsx-eslint/eslint-plugin-reactドキュメント

  • @sdorraさんのGitHub Issue

    const People = ({ params }: Props) => {
      const people = use(fetchPeople(params.id));
      return (
        <>
          <h1>{people.name}</h1>
        </>
      );
    };
    
    export default People;
    

    により、エラー Type error: Page "app/people/[id]/page.tsx" does not match the required types of a Next.js Page. が発生した事例

  • @takudooonさんのZenn記事

    const Input = forwardRef((user, ref) => {
      // ...省略...
    )}
    

    により、エラー Component definition is missing display name react/display-nameが発生した事例

  • @acro5pianoさんのQiita記事

    const createTabBarIcon = (iconName: string) => ({ tintColor, focused }: Props) => {
      return (
        <Icons
          name={iconName}
          size={26}
          style={{ color: tintColor }}
        />
      )
    }
    

    により、エラー Component definition is missing display name react/display-name が発生した事例

まだまだ自分も勉強中ですが、お役に立てれば幸いです。

インフォメーション
少しでも情報を発信する身として、質問やコメントには可能な限り丁寧に対応します。
指摘・質問・コメントお待ちしております。

2Like

Comments

  1. @TaikiTkwkbysh

    Questioner

    @nabeken5
    この度はお忙しい中ご教示いただき、誠にありがとうございます。

    アロー関数として書くと(特に私が提供した例では)、信頼できる関数名が使えなくなり、デバッグに支障をきたします。コンポーネントは決してアロー関数であってはなりません。
    Reactでは、アロー関数とそうでない関数の違いは他にありません。

    ファイルがページであることを意図している場合は、React Component で export ではなく export default を使用していることを再確認してください。すでに export default を使用している場合は、返される値が有効な React Component であることを確認してください。

    なるほど、Next.jsとeslint-plugin-reactにそんな仕様があったんですね。
    なぜ突然そんなことを言い出したのかよく分かりませんが、承知いたしました。

    この度はご対応いただき、誠にありがとうございます。
    また機会がございましたら、ご教示のほど宜しくお願い致します。

  2. この度はお忙しい中ご教示いただき、誠にありがとうございます。

    いえいえ、こちらは絶賛夏休みなので、気にしないでください。
    こちらこそ、React・Next.jsについて深く知れました。
    また機会があったら、よろしくお願いします。

Your answer might help someone💌