メインコンテンツまでスキップ

コンポーネント

この章では、Reactの基本単位であるコンポーネントについて学びます。

この章で学ぶこと

  • コンポーネントとは何か、なぜ使うのか
  • 関数コンポーネントの書き方
  • コンポーネントの分割と再利用
  • ファイル分割のベストプラクティス
備考

この章では03章で作成したプロジェクトを使用します。npm run devで開発サーバーを起動し、コードを書きながら学習を進めましょう。

コンポーネントとは

コンポーネントは、UIを独立した再利用可能なパーツに分割したものです。ボタン、フォーム、カードなど、あらゆるUI要素をコンポーネントとして定義できます。

備考

なぜコンポーネントに分割するのでしょうか?主な理由は以下の3つです:

  1. 再利用性: 同じUIパーツを複数箇所で使い回せる
  2. 保守性: 変更が必要な箇所が局所化される
  3. 可読性: 大きなUIを小さな単位で理解できる

関数コンポーネント

現在のReactでは、関数コンポーネントが標準です。

function Greeting() {
return <h1>こんにちは!</h1>
}

アロー関数でも記述できます。※アロー関数については02章で解説しています。

const Greeting = () => {
return <h1>こんにちは!</h1>
}
備考

React.FC は使わない

型定義の話を先取りすると、関数コンポーネントに React.FC<Props> という型を付ける書き方を見かけることがありますが、本書では使いません。引数の型を直接指定するだけで十分で、React.FC にはいくつか不便な点があります。

// 推奨: 引数の型を直接指定
function Greeting({ name }: { name: string }) {
return <h1>こんにちは、{name}さん!</h1>
}

// 非推奨: React.FC
const Greeting: React.FC<{ name: string }> = ({ name }) => {
return <h1>こんにちは、{name}さん!</h1>
}

React 公式ドキュメントや TypeScript コミュニティでも、近年は React.FC を使わないスタイルが主流です。

関数コンポーネント vs クラスコンポーネント

React 16.8以前は「クラスコンポーネント」が主流でしたが、Hooks(07章〜)の登場により関数コンポーネントが標準になりました。

種類特徴
関数コンポーネントシンプル、Hooks使用可、現在の標準
クラスコンポーネント複雑、レガシーコードで見かける

クラスコンポーネントは24章で解説しますが、新規開発では関数コンポーネントを使用してください。

コンポーネントの命名規則

コンポーネント名はパスカルケース(先頭大文字)で命名します。

// OK: パスカルケース
function UserProfile() { /* ... */ }
function ShoppingCart() { /* ... */ }

// NG: 小文字始まり(HTMLタグと区別できない)
function userProfile() { /* ... */ }
警告

小文字で始まるコンポーネント名はHTMLタグとして解釈されます。<button>はHTML要素ですが、<Button>はReactコンポーネントです。この違いは非常に重要です。

コンポーネントの使用

コンポーネントはHTMLタグのように使用します。

function App() {
return (
<div>
<Greeting />
<Greeting />
</div>
)
}

試してみよう: コンポーネントを作って使う

src/App.tsxを以下のように書き換えて、Greetingコンポーネントを定義し、複数回使用してみましょう。

ヒント
  1. function Greeting()でコンポーネントを定義
  2. JSXで<Greeting />を複数回配置
回答と解説
// src/App.tsx
import './App.css'

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

function App() {
return (
<div>
<h1>挨拶アプリ</h1>
<Greeting />
<Greeting />
<Greeting />
</div>
)
}

export default App

コンポーネントは何度でも再利用できます。<Greeting />を3回配置すると、「こんにちは!」が3回表示されます。これがコンポーネントの再利用性です。

コンポーネントの分割

大きなコンポーネントは、小さなコンポーネントに分割することで管理しやすくなります。

分割前

function App() {
return (
<div>
<header>
<nav>
<a href="/">ホーム</a>
<a href="/about">About</a>
</nav>
</header>
<main>
<h1>ようこそ</h1>
<p>サイトの説明文...</p>
</main>
<footer>
<p>&copy; {new Date().getFullYear()} My Site</p>
</footer>
</div>
)
}

export default App

分割後

function Header() {
return (
<header>
<nav>
<a href="/">ホーム</a>
<a href="/about">About</a>
</nav>
</header>
)
}

function Main() {
return (
<main>
<h1>ようこそ</h1>
<p>サイトの説明文...</p>
</main>
)
}

function Footer() {
return (
<footer>
<p>&copy; {new Date().getFullYear()} My Site</p>
</footer>
)
}

function App() {
return (
<div>
<Header />
<Main />
<Footer />
</div>
)
}

export default App

ファイル分割

コンポーネントは別ファイルに分割し、import/exportで使用します。

src/
├── components/
│ ├── Header.tsx
│ ├── Main.tsx
│ └── Footer.tsx
└── App.tsx
// src/components/Header.tsx
export function Header() {
return (
<header>
<nav>
<a href="/">ホーム</a>
<a href="/about">About</a>
</nav>
</header>
)
}
// src/App.tsx
import { Header } from './components/Header'
import { Main } from './components/Main'
import { Footer } from './components/Footer'

function App() {
return (
<div>
<Header />
<Main />
<Footer />
</div>
)
}

export default App

試してみよう: ファイル分割を実践する

以下の手順でコンポーネントをファイル分割してみましょう。

  1. src/components/Header.tsxを作成し、サイトのタイトルとナビゲーションを表示
  2. src/components/Footer.tsxを作成し、コピーライトを表示
  3. src/App.tsxでインポートして使用
ヒント
  1. export function Header()で関数を定義し、<header>タグ内にタイトルとナビゲーションを配置
  2. export function Footer()で関数を定義し、<footer>タグ内にコピーライトを配置
  3. import { Header } from './components/Header'でインポートし、<Header />として使用
回答と解説

Header.tsx

// src/components/Header.tsx
export function Header() {
return (
<header>
<h1>My App</h1>
<nav>
<a href="/">ホーム</a>
<a href="/about">About</a>
</nav>
</header>
)
}

exportキーワードを付けることで、他のファイルからインポートできます。


Footer.tsx

// src/components/Footer.tsx
export function Footer() {
return (
<footer>
<p>&copy; {new Date().getFullYear()} My App. All rights reserved.</p>
</footer>
)
}

&copy;はHTMLエンティティで、©記号を表示します。


App.tsx

// src/App.tsx
import './App.css'
import { Header } from './components/Header'
import { Footer } from './components/Footer'

function App() {
return (
<div>
<Header />
<main>
<h2>ようこそ</h2>
<p>メインコンテンツがここに入ります</p>
</main>
<Footer />
</div>
)
}

export default App

インポートパスは相対パス(./から始まる)で指定します。コンポーネントは自己終了タグ<Header />で使用するのが一般的です。

コンポーネント設計のポイント

1. 単一責任

各コンポーネントは1つの責任を持つようにします。

2. 再利用性

汎用的に使えるコンポーネントを意識して設計します。

3. 適切な粒度

細かすぎず、大きすぎない適切な粒度で分割します。

備考

コンポーネントの分割に正解はありません。プロジェクトの規模やチームの方針に合わせて調整しましょう。

いつコンポーネントを分割すべきか?

以下のような場合に分割を検討しましょう:

  • 同じUIパターンが2回以上出現する → 再利用のため分割
  • 1つのコンポーネントが100行を超える → 可読性のため分割
  • 複数の責任を持っている → 単一責任のため分割
  • テストが書きにくい → テスタビリティのため分割

逆に、分割しすぎると「props drilling」(propsのバケツリレー)が発生し、かえって複雑になることもあります。※props drillingの解決策は13章(useContext)で解説します。

まとめ

  • コンポーネントはUIを再利用可能な単位に分割したもの
  • 関数コンポーネントが現在の標準
  • 名前はパスカルケースで命名
  • 適切な粒度でファイル分割する

次の章では、コンポーネントにデータを渡すPropsについて学びます。

次に読む