基本コンポーネントを作る【実践】
この節で学ぶこと
この節では、初めて自分でコンポーネントを作る体験を、手を動かしながら進めます。
React 公式では、コンポーネントは UI の再利用できる部品であり、JavaScript の関数として定義して使うと説明されています。
さらに、React の学習導線では「最初のコンポーネント」「props」「イベント処理」を順に学ぶ構成になっています。
この実践の到達目標は4つです。
- 1つの小さな表示部品を自分で作れることです。
- props を使って中身を差し替えられることです。
- クリックイベントを付けて「反応する部品」にできることです。
- Next.js の公式コンポーネント一覧ページを見て、次に何を学ぶとよいか見通しを持てることです。
Next.js 公式には next/image、next/link、next/form、next/script、next/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 名と受け取り側の名前が一致しているか
titleとmessageの型が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.tsx が handleGreeting を持ち、それを 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を表示するchildrenはReactNode型にする
答え
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 がいちばん使いやすいです。ここには Image、Link、Form、Script、Font などが並んでいます。
特に最初に見ると良いページは次のページです。
- Components - API Reference
Next.js のコンポーネント一覧。
- Image Component
next/imageの基本。画像最適化付き。 - Link Component
next/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.
