こんにちは、かつコーチです。
前回はJSXの基本的な書き方を解説しました。
今回は、Reactの一番の核となる考え方である「コンポーネント」について、実際に手を動かしながら理解していきましょう。
コンポーネントとは?
UIを構成する「部品」の単位
コンポーネントとは、ボタンやカード、ヘッダーなど、UIの一部分を1つの独立した「部品」として切り出したものです。
第1回でも触れましたが、Reactは「画面全体を1つの大きなコードで書く」のではなく、「小さな部品を組み合わせて画面を作る」という考え方をとります。
たとえばブログサイトなら、次のような単位でコンポーネントを分けることができます。
Header:サイト上部のナビゲーションArticleCard:記事一覧の1件分のカードFooter:サイト下部のフッター
それぞれを独立した部品として作っておけば、別のページでも同じ部品をそのまま使い回せます。
なぜ部品化するのか
部品化のメリットは、大きく分けて2つあります。
1つ目は再利用性です。
同じ見た目のカードを10個表示したいとき、1つのコンポーネントを作っておけば、あとは呼び出す回数を変えるだけで済みます。
2つ目は保守性です。
「ボタンのデザインを変えたい」となったとき、ボタンのコンポーネント1箇所を修正すれば、それを使っているすべての画面に反映されます。
規模の大きいアプリになるほど、この2つのメリットが効いてきます。
関数コンポーネントの作り方
基本の書き方
現在のReactでは、コンポーネントは関数として定義するのが標準的な書き方です(これを「関数コンポーネント」と呼びます)。
// Greeting.tsx
function Greeting() {
return <p>こんにちは、かつコーチです</p>;
}
export default Greeting;
ルールはシンプルで、次の2点さえ守れば「コンポーネント」として成立します。
- 関数名を大文字から始める(例:
Greeting。小文字始まりだとReactは通常のHTMLタグと誤認識してしまう) - 関数の中でJSXを
returnする
作ったコンポーネントを呼び出す
作成したコンポーネントは、他のファイルからimportして、JSXタグのように呼び出せます。
// App.tsx
import Greeting from "./Greeting";
function App() {
return (
<div>
<Greeting />
<Greeting />
<Greeting />
</div>
);
}
export default App;
<Greeting />と書くだけで、Greetingコンポーネントの中身がその場所に展開されます。
同じコンポーネントを3回呼び出せば、「こんにちは、かつコーチです」という表示が3回分表示されます。
これが、コンポーネントを「再利用できる部品」として使う一番シンプルな例です。
コンポーネントを分割する考え方
1ファイル1コンポーネントが基本
コンポーネントは、基本的に1つのファイルに1つという形で作っていきます。
ファイル名とコンポーネント名を一致させておくと、後で見返したときに管理しやすくなります。
src/
components/
Header.tsx
ArticleCard.tsx
Footer.tsx
App.tsx
componentsのようなフォルダにまとめておくと、規模が大きくなってもファイルを見失いにくくなります。
どこまで分割すればいいのか
コンポーネント設計で初心者がよく悩むのが、「どこまで細かく分けるべきか」という粒度の問題です。
私も学習し始めの頃、「何でもかんでも別コンポーネントに分ければいい」と思い込んで、1つのボタンだけのために新しいファイルを作りすぎてしまい、逆にファイル間を行き来するのが大変になった経験があります。
目安としては、次のような基準で考えるとバランスが取りやすいです。
- 複数の場所で使い回す部分は、迷わずコンポーネントとして切り出す
- 見た目のまとまりとして意味を持つ部分(カード、モーダルなど)は分ける候補にする
- 1回しか使わない、かつ短い部分は、無理に分けずそのまま書いても問題ない
最初は「なんとなく大きすぎるな」と感じたところから分割していく、くらいの気軽さで十分です。
分割の粒度に正解はなく、慣れてくると自然とちょうどいいサイズ感がつかめるようになります。
コンポーネントに複数の要素をまとめる例
実践的な例
ここまでの内容を踏まえて、少し実践的なコンポーネントを作ってみましょう。
// ProfileCard.tsx
function ProfileCard() {
const name: string = "かつコーチ";
const role: string = "Webエンジニア";
return (
<div className="profile-card">
<h2>{name}</h2>
<p>{role}</p>
</div>
);
}
export default ProfileCard;
// App.tsx
import ProfileCard from "./ProfileCard";
function App() {
return (
<div>
<h1>メンバー紹介</h1>
<ProfileCard />
</div>
);
}
export default App;
このように、コンポーネント内部で変数を定義し、JSXの中で{ }を使って表示する、という流れがReactの基本パターンです。
現時点では、ProfileCardの中身(名前や役職)は固定されたままです。
「呼び出す側から名前を自由に変えられたら便利なのに」と思った方は、鋭い感覚を持っています。
その仕組みこそが次回解説する「Props」です。
つまずきやすいポイント:exportの書き忘れ
インポートエラーの原因
コンポーネントを別ファイルに分けたときに私がよくやってしまうミスが、exportの書き忘れです。
❌ Before:exportを書き忘れる
// Greeting.tsx
function Greeting() {
return <p>こんにちは</p>;
}
このままだと、App.tsx側でimport Greeting from "./Greeting"と書いても、Greetingが見つからずエラーになります。
✅ After:export defaultを忘れずに書く
// Greeting.tsx
function Greeting() {
return <p>こんにちは</p>;
}
export default Greeting;
エラーメッセージが「モジュールが見つかりません」のような分かりにくい表現になることもあり、私も原因がexport忘れだと気づくまでに少し時間がかかったことがあります。
新しいコンポーネントファイルを作ったら、まずexport defaultを書く、という順番を先に済ませてしまうと、うっかり忘れを防げます。
まとめ
この記事のポイント
- コンポーネントとは、UIを部品単位に切り出した独立したパーツのこと
- 関数コンポーネントは、大文字始まりの関数を定義し、JSXを
returnすることで作れる - 基本は1ファイル1コンポーネントで管理し、使い回す部分から分割していく
- コンポーネントを別ファイルに分けたら、
export defaultの書き忘れに注意する
次に読むべき記事
コンポーネントの作り方が分かったところで、次回は呼び出す側から値を渡せるようにする「Props」の前段階として、Reactが速く動く理由「仮想DOM」を解説します。
→ 次の記事:仮想DOMとは?Reactが速い理由を理解する