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

基本コンポーネントを作る【実践】

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

この節で学ぶこと

この節では、初めて自分でコンポーネントを作る体験を、手を動かしながら進めます。

React 公式では、コンポーネントは UI の再利用できる部品であり、JavaScript の関数として定義して使うと説明されています。

さらに、React の学習導線では「最初のコンポーネント」「props」「イベント処理」を順に学ぶ構成になっています。

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

  1. 1つの小さな表示部品を自分で作れることです。
  2. props を使って中身を差し替えられることです。
  3. クリックイベントを付けて「反応する部品」にできることです。
  4. Next.js の公式コンポーネント一覧ページを見て、次に何を学ぶとよいか見通しを持てることです。

Next.js 公式には next/imagenext/linknext/formnext/scriptnext/font などのコンポーネント API 一覧があります。

最初に見ておきたい考え方

コンポーネントは画面の部品

React 公式では、コンポーネントは UI の一部分であり、小さなボタンにも大きなページ全体にもなり得ると説明されています。

つまり、コンポーネントを作るとは、画面を意味のある部品へ分けることです。今回は、いきなり難しいものを作りません。

まずは「ラベルを表示する部品」、次に「props を受け取る部品」、最後に「クリックに反応する部品」という順番で進みます。この順番は、React 公式の基礎導線とも自然につながっています。

Next.js の中でも、やっていることは React コンポーネント作成

Next.js は React フレームワークであり、React Components で UI を作り、Next.js が追加機能や最適化を担当すると公式に説明されています。

したがって、app/page.tsx に書くものも、まずは React コンポーネントです。

つまり今やる練習は、「React の練習」であると同時に「Next.js のページを作る基礎練習」でもあります。ここがつながると、学習がかなり楽になります。

実践の準備

置き場所の考え方

Next.js の Project Structure では、app がルーティングとページの中心であり、プロジェクト整理のための推奨構成が案内されています。

再利用部品は components ディレクトリへ分ける構成がよく使われます。

今回は次のような構成で進めます。

src/
  app/
    page.tsx
  components/
    WelcomeCard.tsx
    ActionButton.tsx

app/page.tsx が画面の入口、components/ が再利用部品の置き場所、という分け方です。これは初心者にもかなり見通しがよい構成です。

Client Component が必要な場面

Next.js では、イベントハンドラや state などのインタラクティブな処理は Client Components 側で使うのが基本です。

公式でも、イベントハンドラや state は Client Component の代表例として説明されています。

そのため、クリックイベントを付ける部品では、ファイル先頭に 'use client' を付けます。今回はそこも含めて体験します。

実践1 文字を表示するコンポーネントを作る

手順

まず、src/components/WelcomeCard.tsx を作ります。React 公式では、コンポーネントは JavaScript の関数として定義し、マークアップを返すと説明されています。

export default function WelcomeCard() {
  return (
    <section>
      <h2>ようこそ</h2>
      <p>はじめてのコンポーネント作成です。</p>
    </section>
  )
}

次に、src/app/page.tsx で読み込みます。React 公式では、コンポーネントは import / export を通して別ファイルから使えると説明しています。

import WelcomeCard from '@/components/WelcomeCard'

export default function Page() {
  return (
    <main>
      <h1>トップページ</h1>
      <WelcomeCard />
    </main>
  )
}

これで、WelcomeCard という自作部品をページへ置けました。

見た目は小さいですが、ここで「定義する」「export する」「import する」「使う」という流れが一通りつながっています。

ここで確認したいこと

  • コンポーネント名は大文字始まりか
  • JSX を return しているか
  • page.tsx 側で import して使えているか

React 公式は、コンポーネント名は大文字で始める必要があると説明しています。これが崩れると、React は HTML タグと勘違いすることがあります。

実践2 Props を使って中身を変える

手順

次は、固定表示の部品を「外から内容を変えられる部品」へ進化させます。React 公式では、props は親コンポーネントから子コンポーネントへデータを渡す仕組みだと説明されています。

WelcomeCard.tsx を次のように書き換えます。

type WelcomeCardProps = {
  title: string
  message: string
}

export default function WelcomeCard({
  title,
  message,
}: WelcomeCardProps) {
  return (
    <section>
      <h2>{title}</h2>
      <p>{message}</p>
    </section>
  )
}

page.tsx も書き換えます。

import WelcomeCard from '@/components/WelcomeCard'

export default function Page() {
  return (
    <main>
      <h1>トップページ</h1>

      <WelcomeCard
        title="ようこそ"
        message="はじめての props 練習です。"
      />

      <WelcomeCard
        title="次の一歩"
        message="同じ部品を中身だけ変えて再利用できます。"
      />
    </main>
  )
}

これで、同じ WelcomeCard を2回使いながら、中身だけ変えられるようになりました。React 公式も、props によって同じコンポーネントを異なるデータで再利用できると説明しています。

ここで確認したいこと

  • props 名と受け取り側の名前が一致しているか
  • titlemessage の型が string になっているか
  • JSX の中で {title} {message} として表示できているか

TypeScript 公式では、オブジェクト型や type alias を使って、受け取るデータの形を明示できると説明しています。props 型定義は、初心者の段階でもかなり効果的です。

実践3 クリックできるコンポーネントを作る

手順

次はイベント処理です。React 公式では、イベントハンドラは JSX に追加する関数であり、クリックなどの操作に応じて実行されると説明しています。

src/components/ActionButton.tsx を作成します。

'use client'

type ActionButtonProps = {
  label: string
  onAction: () => void
}

export default function ActionButton({
  label,
  onAction,
}: ActionButtonProps) {
  return <button onClick={onAction}>{label}</button>
}

次に page.tsx を書き換えます。

import WelcomeCard from '@/components/WelcomeCard'
import ActionButton from '@/components/ActionButton'

export default function Page() {
  function handleGreeting() {
    alert('こんにちは。クリックされました。')
  }

  return (
    <main>
      <h1>トップページ</h1>

      <WelcomeCard
        title="ようこそ"
        message="イベント処理の練習を始めます。"
      />

      <ActionButton
        label="押してみる"
        onAction={handleGreeting}
      />
    </main>
  )
}

ここでは、親である page.tsxhandleGreeting を持ち、それを ActionButton に props として渡しています。React 公式でも、親が子へイベントハンドラを props として渡す設計が紹介されています。

ここで確認したいこと

  • ActionButton.tsx'use client' があるか
  • onClick={onAction} と、関数を渡す形になっているか
  • onClick={onAction()} のように、その場で実行していないか

React 公式は、イベントハンドラは passed, not called、つまり「渡すのであって、その場で呼ぶのではない」と強調しています。

実践4 children を使う

手順

次は、children を使って中身を自由に差し込める部品を作ります。React 公式では、ネストした JSX は children props としてコンポーネントへ渡されると説明しています。Fragment のページでも、要素を props として渡せることが示されています。

src/components/Panel.tsx を作ります。

import type { ReactNode } from 'react'

type PanelProps = {
  title: string
  children: ReactNode
}

export default function Panel({ title, children }: PanelProps) {
  return (
    <section>
      <h2>{title}</h2>
      <div>{children}</div>
    </section>
  )
}

page.tsx で使います。

import WelcomeCard from '@/components/WelcomeCard'
import ActionButton from '@/components/ActionButton'
import Panel from '@/components/Panel'

export default function Page() {
  function handleGreeting() {
    alert('こんにちは。クリックされました。')
  }

  return (
    <main>
      <h1>トップページ</h1>

      <Panel title="学習メモ">
        <p>children を使うと、枠だけ共通化できます。</p>
        <ActionButton
          label="押してみる"
          onAction={handleGreeting}
        />
      </Panel>

      <WelcomeCard
        title="まとめ"
        message="コンポーネントを組み合わせて画面を作れます。"
      />
    </main>
  )
}

これで Panel は「タイトル付きの枠」として再利用できるようになります。中に何を入れるかは使う側が決められます。React 公式でも、children はラッパー系コンポーネントで特に有効だと説明されています。

練習問題

問1

HelloCard というコンポーネントを作ってください。

条件は次のとおりです。

  • 見出しに「Hello」
  • 本文に「最初の練習です」
  • export default function HelloCard() の形で書く

答え

export default function HelloCard() {
  return (
    <section>
      <h2>Hello</h2>
      <p>最初の練習です</p>
    </section>
  )
}

解説

React 公式では、コンポーネントは JavaScript の関数として定義し、マークアップを返すと説明されています。まずは props なしの固定部品を作るのがよい練習です。

問2

ProfileCard というコンポーネントを作ってください。

条件は次のとおりです。

  • name を props で受け取る
  • <h2>name を表示する
  • TypeScript で型を付ける

答え

type ProfileCardProps = {
  name: string
}

export default function ProfileCard({
  name,
}: ProfileCardProps) {
  return (
    <section>
      <h2>{name}</h2>
    </section>
  )
}

解説

React 公式では、props は親から子へデータを渡す仕組みです。TypeScript で型を付けると、その部品が何を受け取るのかが明確になります。

問3

次のコードの問題点を答えてください。

'use client'

type ButtonProps = {
  onAction: () => void
}

export default function Button({ onAction }: ButtonProps) {
  return <button onClick={onAction()}>押す</button>
}

答え

onClick に 関数を渡す べきなのに、onAction() と書いて その場で実行している のが問題です。

修正版は次です。

'use client'

type ButtonProps = {
  onAction: () => void
}

export default function Button({ onAction }: ButtonProps) {
  return <button onClick={onAction}>押す</button>
}

解説

React 公式では、イベントハンドラは passed, not called と説明されています。onClick={onAction} は正しく、onClick={onAction()} はレンダリング時に実行されてしまいます。

問4

Card というコンポーネントを作ってください。

条件は次のとおりです。

  • children を受け取る
  • <div> の中に children を表示する
  • childrenReactNode 型にする

答え

import type { ReactNode } from 'react'

type CardProps = {
  children: ReactNode
}

export default function Card({ children }: CardProps) {
  return <div>{children}</div>
}

解説

React では、コンポーネントタグの中へネストした内容は children props として渡されます。ラッパー系コンポーネントの基本形です。

問5

次のようなコンポーネントを作りたいです。

  • タイトル付きの枠
  • 中身は自由に差し込める
  • ボタンや文章も入れられる

このとき使うとよい props は何ですか。

答え

children を使うとよいです。

解説

React 公式では、children はネストした JSX を受け取るための特別な props として説明されています。枠だけ共通化して中身を自由にしたい場面で特に有効です。

参考になる外部ページ

React の公式学習ページ

最初のコンポーネント作成や props の感覚を確認するには、次の React 公式ページが特に参考になります。

Next.js の公式コンポーネント一覧ページ

Next.js の「標準で使えるコンポーネント」をまとめて見るには、Components - API Reference がいちばん使いやすいです。ここには ImageLinkFormScriptFont などが並んでいます。

特に最初に見ると良いページは次のページです。

  • Components - API Reference

Next.js のコンポーネント一覧。

  • Image Componentnext/image の基本。画像最適化付き。
  • Link Componentnext/link の基本。ルーティング用。
  • Architecture

コンポーネントやファイル規約を広く見渡したいときに有用。

まとめ

初めてコンポーネントを作るときにいちばん大切なのは、完璧な部品を作ることではなく、小さく作って、ページで使って、少しずつ再利用できる形へ寄せることです。

React 公式の導線でも、最初のコンポーネント、props、イベント処理、UI の分解という順番で学ぶようになっています。

今回の実践で押さえたいのは次の4点です。

  • コンポーネントは JavaScript / TypeScriptの関数として作る。
  • props を使うと同じ部品を中身だけ変えて再利用できる。
  • イベント処理を使う部品は Next.js では Client Component 側に置く。
  • children を使うと、枠だけ共通化して中身を自由に差し込める。

ここまできたら、手を動かして経験を積むだけになります。試行錯誤しながら進めていってください。

参考文献

  • React Docs, Your First Component.
  • React Docs, Passing Props to a Component.
  • React Docs, Quick Start.
  • React Docs, Thinking in React.
  • React Docs, Fragment.
  • Next.js Docs, Components - API Reference.
  • Next.js Docs, Image Component.
  • Next.js Docs, Link Component.
  • Next.js Docs, Architecture.
  • Next.js Docs, Getting Started: Server and Client Components.
教材トップへ戻る