Next.jsによるWebアプリケーション開発概論

Reactコンポーネントの基本構造

2Next.js基礎:ReactコンポーネントとJSX・Propsの基本
Next.jsTailwind CSSアプリ開発開発Web開発

この節で学ぶこと

この節では、Reactコンポーネントの基本構造の流れがつかめるように整理します。React 公式では、コンポーネントは UI の土台であり、JavaScript の関数として定義し、マークアップを返して使うものだと説明されています。さらに、React ではコンポーネントをネストして大きな画面を組み立てられます。

この節の到達目標は4つです。

  1. Reactコンポーネントが「JavaScript の関数 + JSX の戻り値」でできていると説明できることです。
  2. 定義する・書き出す・使うという基本の流れを理解することです。
  3. コンポーネント名が大文字で始まる理由や、トップレベルで定義する理由を説明できることです。
  4. Next.js の page.tsxlayout.tsx も、Reactコンポーネントとして成り立っているとつながって見えることです。

React 公式と Next.js 公式の両方で、この考え方が基礎として扱われています。

Reactコンポーネントとは何か

UIを作るための部品

React 公式では、コンポーネントは再利用できる UI 要素であり、React アプリはコンポーネントでできていると説明されています。

小さなボタンも、大きなページ全体も、どちらもコンポーネントとして扱えます。

ここで大事なのは、コンポーネントを「特別な魔法の箱」と思わないことです。見た目を返す JavaScript の関数です。ただし、

その関数は UI の部品として使うため、名前の付け方や戻り値の形に React 独自の約束があります。React 公式も、コンポーネントは regular JavaScript functions だと説明しています。

画面を丸ごとではなく、部分で考える

React 公式では、コンポーネントを組み合わせ、順番に並べ、ネストして画面全体を設計できると説明されています。たとえば PageLayout の中に NavigationHeaderSidebarPageContent が入る、といった構造です。

この考え方がなぜ大切かというと、実際の画面は繰り返しの集合だからです。

見出し、カード、ボタン、一覧、フォーム。こうしたまとまりを毎回ベタ書きするより、部品として定義して使い回した方が、直しやすく、読みやすく、再利用しやすくなります。

React 公式でも、既に書いたコンポーネントを再利用して開発を速くできると説明しています。

Reactコンポーネントの最小構造

いちばん小さな形

React 公式の最初の例は、export default function Profile() { ... } の形です。これを分解すると、Reactコンポーネントの基本構造は次の3つです。書き出し、関数定義、マークアップの return です。

export default function MyButton() {
  return <button>送信</button>
}

この短いコードの中に、Reactコンポーネントの骨格がほぼ全部入っています。

  • export default は、他のファイルから読み込めるようにする JavaScript の export 構文です。React 固有ではありません。React 公式も、これは standard JavaScript syntax だと説明しています。
  • function MyButton() {} は、MyButton という JavaScript 関数です。
  • return <button>送信</button> は、表示したいマークアップを返しています。この記法は JSX です。

関数なのに、見た目を返す

普通の JavaScript 関数は数値や文字列を返すことが多いです。

でも Reactコンポーネントは、画面として表示するための JSX を返します。React 公式は、コンポーネントは “functions that return markup” だと説明しています。

たとえば次の関数は、計算結果ではなく UI を返しています。

function WelcomeMessage() {
  return <p>ようこそ</p>
}

この時点で、Reactコンポーネントは「プログラムの関数」でありながら、「UI の設計単位」でもあります。この二重の性質が、React の面白いところです。

コンポーネント名とHTMLタグの違い

コンポーネント名は大文字で始める

React 公式では、React コンポーネントの名前は必ず大文字で始める必要があると説明されています。そうでないと、React はそれを独自コンポーネントではなく HTML タグだと解釈します。

たとえば、次の2つは意味が違います。

function MyButton() {
  return <button>送信</button>
}
export default function Page() {
  return (
    <div>
      <button>HTMLのbutton</button>
      <MyButton />
    </div>
  )
}

ここで、button はブラウザにもともとある HTML 要素です。

一方、MyButton は自分で作った React コンポーネントです。React 公式も、<section> は lowercase だから HTML tag、<Profile /> は capital letter だから React component だと説明しています。

見た目は似ていても意味が違う

JSX ではどちらもタグのように見えますが、React の中では別物です。

この違いが分かるようになると、コードを読んだときに「これはブラウザ標準の要素なのか」「自作の部品なのか」がすぐ見えるようになります。ここは初心者が最初につかむべき、とても大切な感覚です。

return の書き方

1行ならそのまま返せる

React 公式では、返す JSX が1行なら、次のようにシンプルに書けると説明しています。

function Avatar() {
  return <img src="/avatar.png" alt="avatar" />
}

これはとても読みやすいです。

ただし、見た目が少し複雑になると、ほとんどの場面では改行したくなります。

複数行なら丸括弧で包む

React 公式では、return の後が複数行になる場合は、丸括弧で包む必要があると説明しています。また、丸括弧がないと return の後の改行以降が無視されることがあると注意しています。

function ProfileCard() {
  return (
    <section>
      <h2>プロフィール</h2>
      <p>自己紹介がここに入ります。</p>
    </section>
  )
}

これは書式の好みではなく、かなり実務的なルールです。

初心者のうちは「なぜわざわざ括弧を付けるのか」と思いがちですが、改行した JSX を安全に返すためです。React 公式の Pitfall にも、この点がはっきり書かれています。

コンポーネントを使う

定義したら、タグのように呼び出せる

React 公式では、定義したコンポーネントは別のコンポーネントの中でタグのように使えると説明しています。たとえば Profile を複数回呼び出して Gallery を構成する例が示されています。

function Profile() {
  return <img src="/avatar.png" alt="avatar" />
}

export default function Gallery() {
  return (
    <section>
      <h1>メンバー一覧</h1>
      <Profile />
      <Profile />
      <Profile />
    </section>
  )
}

このコードを見ると、コンポーネントの価値がかなりよく分かります。

Profile を1回定義するだけで、何度でも使えます。React 公式も、コンポーネントを一度定義すれば as many times as you like 使えると説明しています。

親と子の関係で考える

この例では、Gallery が親コンポーネント、Profile が子コンポーネントです。React 公式も、Gallery が each Profile を child として render していると説明しています。

この親子関係の見方は、このあと props や children を学ぶときに非常に重要になります。

今はまず、「大きな部品の中に小さな部品を置ける」という感覚を持てれば十分です。

1ファイルに複数コンポーネントを書く

関連が深いなら同じファイルでもよい

React 公式では、コンポーネントは regular JavaScript functions なので、複数のコンポーネントを同じファイルに置くこともできると説明しています。特に、それらが小さいか、強く関連している場合は便利だとも書かれています。

function Avatar() {
  return <img src="/avatar.png" alt="avatar" />
}

function UserName() {
  return <h2>佐藤太郎</h2>
}

export default function ProfileCard() {
  return (
    <section>
      <Avatar />
      <UserName />
    </section>
  )
}

このように、短くて関係の深い部品なら1ファイルにまとめても自然です。

一方で、ファイルが混み合ってきたら分割して import / export するのがよい、というのも React 公式の説明どおりです。

ただし、定義する場所には注意がいる

ここがかなり重要です。

React 公式は、コンポーネントを別のコンポーネントの中で定義してはいけないと明確に注意しています。これは遅くなったり、バグの原因になったりするためです。代わりに、各コンポーネントは top level で定義するよう案内しています。

悪い例です。

export default function Gallery() {
  function Profile() {
    return <img src="/avatar.png" alt="avatar" />
  }

  return (
    <section>
      <Profile />
    </section>
  )
}

React 公式は、このような “Never define a component inside another component!” という形を避けるように説明しています。

良い例はこちらです。

function Profile() {
  return <img src="/avatar.png" alt="avatar" />
}

export default function Gallery() {
  return (
    <section>
      <Profile />
    </section>
  )
}

このルールは、初心者のうちはつい破りやすいです。

「使う場所の近くに書いた方が分かりやすそう」と感じるからです。でも React の仕組みとしては、コンポーネント定義はトップレベルで覚えておく方が安全です。

import と export の基本構造

他ファイルで使うには export が必要

React 公式の “Importing and Exporting Components” では、コンポーネントを別ファイルで使うには export して import する という基本を説明しています。export には default export と named export があり、読み込み方が異なります。

たとえば、1つのファイルにメインのコンポーネントが1つあるなら、次のように default export がよく使われます。

export default function Button() {
  return <button>送信</button>
}

別ファイルではこう読み込みます。

import Button from './Button'

React 公式も、export defaultimport Component from ... の対応を基本形として説明しています。

named export もある

複数の部品を明示的に書き出したいときは named export を使えます。React 公式でも default export と named export の両方が紹介されています。

export function Header() {
  return <header>ヘッダー</header>
}

export function Footer() {
  return <footer>フッター</footer>
}

読み込む側はこうです。

import { Header, Footer } from './LayoutParts'

最初は default export が分かりやすいですが、named export の形も今後よく出てきます。

ここで重要なのは、「コンポーネントは1つのファイル内だけの存在ではなく、export / import を通じて画面全体へ組み込まれる部品だ」という点です。

Next.js の中で見る Reactコンポーネント

page.tsx も Reactコンポーネント

Next.js の page は route-unique UI を定義するファイルで、App Router におけるページの入口です。つまり app/page.tsx に書いているものも、実体としては Reactコンポーネントです。

たとえば、次のような page.tsx は完全に Reactコンポーネントです。

export default function Page() {
  return (
    <main>
      <h1>トップページ</h1>
      <p>ここは Next.js の page コンポーネントです。</p>
    </main>
  )
}

この見方ができると、「React を学んでいる」と「Next.js を学んでいる」が頭の中でつながります。

Next.js は React ベースのフレームワークなので、ページやレイアウトを含めて、結局は Reactコンポーネントの組み合わせでできています。

layout.tsx も Reactコンポーネント

Next.js の layout は、複数ルートで共有される UI を定義するファイルです。公式では children を受け取り、ネストされたレイアウトやページを包む仕組みとして説明されています。

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="ja">
      <body>{children}</body>
    </html>
  )
}

ここでも、やっていることは Reactコンポーネントの基本構造と同じです。

関数を定義して、JSX を返している。違うのは、役割が「ページ全体を包む」ことに寄っているだけです。

初心者がつまずきやすい点

関数なら何でもコンポーネントではない

JavaScript の関数であっても、React コンポーネントとして使うなら、React の約束に従う必要があります。

たとえば大文字で始めること、JSX を返すこと、コンポーネントとして呼び出すことです。React 公式でも、コンポーネント名は capital letter で始める必要があると説明しています。

return の後に改行して壊れる

React 公式が Pitfall として挙げているように、return の後に改行し、括弧で包んでいないと、意図した JSX が返らないことがあります。

悪い例です。

function Sample() {
  return
    <p>こんにちは</p>
}

よい例です。

function Sample() {
  return (
    <p>こんにちは</p>
  )
}

これは React というより JavaScript の return の挙動に関係するので、早めに慣れておくと安心です。

定義場所をネストしない

さきほど触れた通り、コンポーネント定義はトップレベルに置きます。

React 公式は、コンポーネントを別のコンポーネントの中に定義すると slow and causes bugs と明確に書いています。

このルールは、最初のうちは「少し厳しすぎるのでは」と感じるかもしれません。

でも、今の段階で癖にしておくと、後でかなり助かります。

練習問題

問1

Reactコンポーネントの基本的な正体として最も適切なものはどれですか。

A. CSS の設定ファイル

B. JSX を返す JavaScript の関数

C. 画像だけを表示する HTML タグ

D. データベース接続設定

答え

B

解説

React 公式では、React コンポーネントは JavaScript functions that return markup と説明されています。

問2

Reactコンポーネント名について正しいものはどれですか。

A. 小文字で始める必要がある

B. 数字で始める必要がある

C. 大文字で始める必要がある

D. すべて日本語で書く必要がある

答え

C

解説

React 公式では、コンポーネント名は capital letter で始める必要があると説明されています。小文字だと HTML タグとして解釈される可能性があります。

問3

複数行の JSX を return するときの基本として正しいものはどれですか。

A. 何もしなくてよい

B. 文字列に変換する

C. 丸括弧で包む

D. 必ず console.log する

答え

C

解説

React 公式では、return の後に改行を含む JSX を書く場合は丸括弧で包むように説明しています。そうしないと後続の行が無視されることがあります。

問4

React 公式が避けるよう説明していることはどれですか。

A. トップレベルでコンポーネントを定義すること

B. コンポーネントを別のコンポーネントの中で定義すること

C. コンポーネントを複数回使うこと

D. JSX を返すこと

答え

B

解説

React 公式は “Never define a component inside another component!” と説明しており、トップレベルで定義するよう案内しています。

問5

Next.js の app/page.tsx について正しい説明はどれですか。

A. React と無関係な特別形式のファイル

B. CSS 専用ファイル

C. route-unique UI を定義する Reactコンポーネントの入口

D. 環境変数専用ファイル

答え

C

解説

Next.js 公式では page は route-unique UI を定義するファイルであり、App Router のページの入口です。実体としては Reactコンポーネントです。

まとめ

Reactコンポーネントの基本構造は、驚くほどシンプルです。

JavaScript の関数を定義し、JSX を返し、必要なら export して、別の場所でタグのように使う。React 公式では、まさにこの流れが最初の基礎として説明されています。

この節で特に押さえたいのは、次の5点です。

  • コンポーネントは UI の部品である。
  • Reactコンポーネントは JSX を返す JavaScript 関数である。
  • コンポーネント名は大文字で始める。
  • 複数行の JSX は丸括弧で返す。
  • コンポーネント定義はトップレベルに置く。

ここが見えてくると、Next.js の page.tsxlayout.tsx も、急に親しみやすくなります。

難しい新技術に見えていたものが、「Reactコンポーネントを場所ごとに使っているだけ」と理解できるからです。次の節で JSX とレンダリングを学ぶと、この骨格にさらに意味が通ってきます。

参考文献

  • React Docs, Your First Component
  • React Docs, Importing and Exporting Components
  • React Docs, Writing Markup with JSX
  • Next.js Docs, Getting Started: Project Structure
  • Next.js Docs, Getting Started: Layouts and Pages
教材トップへ戻る