みなさん、Next.jsのApp Routerでページコンポーネントを作成するとき、関数宣言とアロー関数のどちらを使うか迷ったことはありませんか?僕も最初は悩みましたが、この記事では両者の使い方と選び方について詳しく解説していきます。
関数宣言での書き方:
export default function Page() {
return <h1>Hello Next.js!</h1>
}
アロー関数での書き方:
const Page = () => {
return <h1>Hello Next.js!</h1>
}
export default Page;
Next.jsの公式ドキュメントではどう書かれているの?
本稿ではまず、Next.jsの公式ドキュメントと例を見ていきましょう。App Routerのページコンポーネントの定義では、主に関数宣言が使用されています。例えば、次のようなコードが示されています:
export default function Page() {
return <h1>Hello Next.js!</h1>
}
興味深いことに、この関数スタイルの定義は、App Routerのドキュメント全体とスターターテンプレートで一貫して使用されているんです1。実際、みなさんがcreate-next-app
を使って新しくNext.js 13+プロジェクトを作成すると、export default function ...
構文でページが自動生成されることに気づくと思います。
ここで気になるのは、「Next.jsは関数宣言を推奨しているのか?」という点ですよね。実は、Next.jsチームは一方のスタイルを他方より優先すべきとは明確に述べていません。むしろ、構文の選択は私たち開発者に委ねられているんです。Next.jsのメンテナーも、「Next.jsにおけるReactコンポーネントでは、アロー関数と従来の関数で動作に実質的な違いはない」としています2。
では、なぜ公式資料では関数宣言スタイルが好まれているのでしょうか?僕が考えるに、これは名前付き関数なら直接デフォルトエクスポートできるといった利便性によるものだと思います。あくまで想像ですが。ここで重要なのは、Next.jsは各ページファイルにデフォルトエクスポートを要求するということです。そのエクスポートが関数宣言であれアロー関数であれ、有効なReactコンポーネントである限り、Next.jsの動作は変わりません3。
実際のプロジェクトではどちらが多く使われているの?
では、実際のプロジェクトではどのように書かれているのでしょうか?僕が調べてみたところ、Next.js 13+のプロジェクトでは両方のパターンが使用されていますが、多くのオープンソースプロジェクトは公式の例に従って関数宣言を使用しているようです。
例えば、Next.js App Routerに関する人気のチュートリアルリポジトリでは、次のようにページを定義しています:
export default function Page() {
return <p>About</p>;
}
これはNext.jsドキュメントのスタイルを反映しています4。同様に、多くのボイラープレートやブログ記事では、ドキュメントやコミュニティのデフォルトに合わせて、function Page() {...}
でページを定義しています。
とはいえ、アロー関数コンポーネントも、特にTypeScriptのコードベースでは一般的です。アロー関数を好む開発者は、多くの場合、コンポーネントを定数として定義し、それをエクスポートします。例えば:
const Page = async ({ data }: Props) => {
return <div>...</div>;
};
export default Page;
あるオープンソースのNext.js 13プロジェクトでは、ページがまさにこの方法で、constアロー関数として定義され、その後デフォルトエクスポートされていました。このアプローチも同じ結果(デフォルトエクスポートされたReactコンポーネント)を達成していますが、アロー関数構文を使用しています。
GitHubリポジトリを観察すると、両方のスタイルの多くの例を見つけることができます。一部のプロジェクトは特定のコーディング標準(例えば、一般的にデフォルトエクスポートを避けるチームもあります)に従っており、それによってアロー関数+個別エクスポートのパターンを使用することになります。要約すると、両方のスタイルが実際に使用されていますが、公式スターターと例での存在により、関数宣言スタイルは非常に広く普及しています。
開発者たちはどう考えているの?
では、この2つのスタイルについて、開発者たちはどのように考えているのでしょうか?僕がフォーラムやQ&Aサイトを調べてみたところ、面白い発見がありました。
多くの開発者が指摘しているのは、これが主にスタイルと好みの問題だということです5。内部的には、Reactでは同じように動作するため、どちらを選んでも問題ないんですね。例えば、あるStack Overflowの回答では、「両方の定義は同じ方法で定義されるため、違いはない...これは完全に好みの問題です」と述べられています5。
アロー関数を好む開発者が多いのは、なぜでしょうか?それは、より簡潔に書けるからなんです。特に、シンプルなUIコンポーネントを書くときは、ES6のアロー関数を使うと暗黙のreturnで短く書けて便利です。この好みは自然とNext.jsプロジェクトにも引き継がれているようです。
一方で、関数宣言を好む開発者もいます。その理由として挙げられているのが、より良いスタックトレースが得られることです(ただし、最近のツールは変数名からアロー関数にも名前を付けられるので、あまり違いはないかもしれません)。また、チームのスタイルガイドで決められているからという理由もあるようです。
特に興味深いのは、Redditでのある開発者のコメントです。「コールバック以外ではほぼ常に関数宣言を使う」と述べており、その理由として、大規模なコードベースでは名前付き関数の方が分かりやすいと説明しています6。
初期のバグと現在の状況
ここで一つ、歴史的な話をしておく必要があります。実は、Next.js 13 App Routerのリリース初期には、開発モードでアロー関数コンポーネントに影響を与える一部のエッジケースバグがあったようです。
例えば、クライアントコンポーネントページでアロー関数のデフォルトエクスポートを使用すると、ハイドレーションエラーが発生することがあったようです7。また、HMR中に完全なページリロードが強制されるという問題も報告されています。
ただし、これらのバグは既に修正されていますので、心配する必要はありません。当時は一部の開発者に「関数宣言の方が安全かも?」という印象を与えましたが、これは一時的な問題だったということですね。
なぜ関数宣言が多く見られるの?
では、なぜNext.js App Routerのコードを見ると、関数宣言が多く使われているのでしょうか?これには、いくつかの理由があると思いました:
-
公式の規約: Next.jsのドキュメントとスターターが
function Page() {}
を使っているため、多くの開発者がそれに従っている。 -
デフォルトエクスポートの簡潔さ: 関数宣言なら
export default function Page(){}
と1行で書けます。アロー関数だと2ステップ必要。 - コミュニティの影響: Next.jsの例やテンプレートで関数宣言が使われているため、特に使い始めたばかりの方々がそのスタイルを採用していった。
まとめ
ここまで見てきて分かるように、「間違った」選択肢はないわけです。関数宣言もアロー関数も、どちらもNext.js App Routerで問題なく動作します。Next.jsのコアメンバーも「実質的にまったく問題ありません」と言っています。
結局のところ、どちらを選ぶかは、あなたやチームが読みやすいと感じるスタイル、または慣れているスタイルで良いと思います。関数宣言が多く見られるのは、技術的な理由というよりも、規約と初期テンプレートの影響によるもの程度の話だと思います。
最後までお読みくださり、ありがとうございました。この記事が、Next.jsでのコーディングスタイルを選ぶ際の参考になれば嬉しいです。もし気になる点や質問があれば、コメントでお知らせください。また、新しい発見があれば、記事を更新していきたいと思います。😊
-
arrow function vs function declaration in Next.js · vercel next.js · Discussion #37835 · GitHub ↩
-
Next.js App Router:ESモジュール export 定義について #AppRouter - Qiita ↩
-
javascript - Why is arrow syntax preferred over function declaration for functional React components? - Stack Overflow ↩ ↩2
-
What scenario would I not use an Arrow function? : r/reactjs - Reddit ↩
-
Hydration error (appDir) when exporting an arrow function in page.js marked as a client component · Issue #48537 · vercel/next.js · GitHub ↩