第6回
propsでデータを渡す:FeatureCardコンポーネントを作る
Reactのpropsの仕組みをTypeScriptの型定義と合わせて解説。コピペから脱してデータを外から受け取るコンポーネントの作り方を学ぶ。
·18分で読める
たける
トップページの「特徴紹介」セクション、カードが3枚あるんですけど、同じHTML構造を3回コピペしてしまいました。
りこ
見せて。
// たけるが書いたコード
function Features() {
return (
<div>
<div className="feature-card">
<span>📖</span>
<h3>対話形式で学べる</h3>
<p>登場人物との会話を通じてReactの概念を自然に習得できます。</p>
</div>
<div className="feature-card">
<span>🛠</span>
<h3>実際に動くものを作る</h3>
<p>概念だけでなく、実際にWebサイトを完成させながら学びます。</p>
</div>
<div className="feature-card">
<span>🚀</span>
<h3>公開まで完走する</h3>
<p>Cloudflare Pagesへのデプロイまで、一連の流れを体験します。</p>
</div>
</div>
)
}
りこ
この3枚、構造は全部同じで中身だけ違う。これがpropsを使うタイミング。
propsとは
props(プロップス)は、コンポーネントに外から渡すデータのこと。関数の引数と同じ考え方で、「形は同じでも、渡す値によって表示が変わる」コンポーネントを作れる。
// 「形」を1回だけ定義する
function FeatureCard({ icon, title, description }) {
return (
<div className="feature-card">
<span>{icon}</span>
<h3>{title}</h3>
<p>{description}</p>
</div>
)
}
// 3枚とも同じコンポーネントで、データだけ変える
function Features() {
return (
<div>
<FeatureCard icon="📖" title="対話形式で学べる" description="登場人物との会話を通じて..." />
<FeatureCard icon="🛠" title="実際に動くものを作る" description="概念だけでなく..." />
<FeatureCard icon="🚀" title="公開まで完走する" description="Cloudflare Pagesへの..." />
</div>
)
}
たける
`{ icon, title, description }` って、引数の中に直接書いてるんですね。オブジェクトの分割代入ですか?
りこ
そう。Reactはpropsをオブジェクトで受け取る。`props.icon`・`props.title` と書いてもいいけど、引数の時点で分割代入するのが定番。
TypeScriptで型を定義する
TypeScriptを使うと、propsに「どんなデータを渡すべきか」を明示できる。型が合わないときはコードを書いた瞬間にエラーが出る。
// src/components/FeatureCard.tsx
// ① propsの型を定義する
type FeatureCardProps = {
icon: string
title: string
description: string
}
// ② 引数に型を書く
export const FeatureCard = ({ icon, title, description }: FeatureCardProps) => {
return (
<div className="feature-card">
<span>{icon}</span>
<h3>{title}</h3>
<p>{description}</p>
</div>
)
}
たける
型をつけると何がうれしいんですか? 書くのが面倒な気もして。
りこ
試してみて。`description` を渡し忘れて `FeatureCard` を使ってみて。
たける
すぐ赤波線が出た。「Property 'description' is missing」って。ブラウザで確認する前に気づけますね。
りこ
それが型の価値。バグが実行前に見つかる。
オプショナルなprops
すべてのpropsが必須とは限らない。省略できるpropsには ? をつける。
type FeatureCardProps = {
icon: string
title: string
description: string
link?: string // ? をつけると省略可能
}
export const FeatureCard = ({ icon, title, description, link }: FeatureCardProps) => {
return (
<div className="feature-card">
<span>{icon}</span>
<h3>{title}</h3>
<p>{description}</p>
{link && <a href={link}>詳しく見る</a>} {/* linkがある場合だけ表示 */}
</div>
)
}完成形:データを配列で管理する
propsが機能したら、データを配列にまとめると後から管理しやすい。
// src/components/Features.tsx
import { FeatureCard } from './FeatureCard'
const features = [
{
id: 'dialogue',
icon: '📖',
title: '対話形式で学べる',
description: '登場人物との会話を通じてReactの概念を自然に習得できます。',
},
{
id: 'hands-on',
icon: '🛠',
title: '実際に動くものを作る',
description: '概念だけでなく、実際にWebサイトを完成させながら学びます。',
},
{
id: 'deploy',
icon: '🚀',
title: '公開まで完走する',
description: 'Cloudflare Pagesへのデプロイまで、一連の流れを体験します。',
},
]
export const Features = () => {
return (
<section>
<h2>このシリーズの特徴</h2>
<div className="features-grid">
{features.map(f => (
<FeatureCard key={f.id} {...f} />
))}
</div>
</section>
)
}
たける
`{...f}` って何ですか? スプレッド演算子ですか?
りこ
そう。オブジェクトのプロパティを全部propsとして展開する書き方。`icon={f.icon} title={f.title} description={f.description}` と1つずつ書くのと同じ意味。データの形とpropsの型が一致しているときに使える省略形。
たける
整理すると──propsは「コンポーネントへの引数」で、配列のデータとmapを組み合わせると「データが増えても同じコンポーネントで対応できる」ってことですね。
りこ
完璧。それがReactのコンポーネント思考の核心。
📁 第6回完了時点のファイル構成・完成コード
フォルダ構成
src/
├── components/
│ ├── Header.tsx
│ ├── Footer.tsx
│ └── FeatureCard.tsx ← 今回追加
└── App.tsx ← FeatureCardを使うように更新FeatureCard.tsx
type FeatureCardProps = {
icon: string
title: string
description: string
}
export const FeatureCard = ({ icon, title, description }: FeatureCardProps) => {
return (
<div className="bg-white rounded-2xl border border-slate-100 shadow-sm p-6">
<span className="text-3xl mb-4 block">{icon}</span>
<h3 className="text-lg font-bold text-slate-900 mb-2">{title}</h3>
<p className="text-slate-600 text-sm leading-7">{description}</p>
</div>
)
}App.tsx(FeatureCardを使う部分)
import { Header } from './components/Header'
import { Footer } from './components/Footer'
import { FeatureCard } from './components/FeatureCard'
function App() {
return (
<div className="min-h-screen flex flex-col">
<Header />
<main className="flex-1">
<section className="max-w-5xl mx-auto px-4 sm:px-8 py-16">
<h2 className="text-2xl font-bold text-slate-900 mb-8 text-center">
このサイトで学べること
</h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<FeatureCard
icon="⚛️"
title="Reactの基礎"
description="コンポーネント・props・stateの概念を実例で理解する。"
/>
<FeatureCard
icon="🎨"
title="デザインシステム"
description="Tailwind CSSでUIを素早く構築する方法を学ぶ。"
/>
<FeatureCard
icon="🚀"
title="デプロイまで"
description="Cloudflare Pagesに公開して、URLを手に入れる。"
/>
</div>
</section>
</main>
<Footer />
</div>
)
}
export default Appまとめ
- props:コンポーネントに外から渡すデータ。関数の引数と同じ考え方
- 引数で分割代入するのが定番:
({ icon, title }: Props) - TypeScriptで型定義すると、渡し忘れや型ミスをコードを書いた時点で検出できる
- オプショナルなpropsには
?をつける {...object}でオブジェクトのプロパティを一括でpropsに展開できる
次の第7回では、Tailwind CSSを使ってトップページのデザインを整える。