| title | 初めてのコンポーネント |
|---|
コンポーネントは、React における最重要コンセプトのひとつです。皆さんがユーザインターフェース (UI) を構築するときの基盤となるものですので、React の旅路はコンポーネントから始めていくことにしましょう!
- コンポーネントとは何か
- React アプリでコンポーネントが果たす役割
- 初めてのコンポーネントの書き方
Web の世界では、HTML で <h1> や <li> といった組み込みタグを使い、リッチで構造化されたドキュメントを作成することができます。
<article>
<h1>My First Component</h1>
<ol>
<li>Components: UI Building Blocks</li>
<li>Defining a Component</li>
<li>Using a Component</li>
</ol>
</article>このマークアップは、記事自身 (<article>)、見出し (<h1>)、そして番号付きリスト (<ol>) による目次(一部省略しています)を表現しています。我々がウェブで目にするサイドバー、アバター、モーダル、ドロップダウンといったあらゆる UI パーツの裏側では、このようなマークアップが、スタイルのための CSS やユーザ対話のための JavaScript と組み合わさりながら働いています。
React では、あなたのマークアップと CSS と JavaScript を、独自の "コンポーネント" と呼ばれる、アプリのための再利用可能な UI 要素にまとめることができます。上記にある目次のためのコードを、<TableOfContents /> と呼ばれるコンポーネントにすることができるのです。その裏では、やはり <article> や <h1> といった同じ HTML タグを使っています。
HTML タグを使う時と同様にして、コンポーネントを組み合わせたり、並び替えたり、ネストしたりして、ページ全体をデザインすることができます。例えばあなたが今読んでいるこのページも、以下のような React コンポーネントから作られています。
<PageLayout>
<NavigationHeader>
<SearchBar />
<Link to="/docs">Docs</Link>
</NavigationHeader>
<Sidebar />
<PageContent>
<TableOfContents />
<DocumentationText />
</PageContent>
</PageLayout>プロジェクトが成長するとともに、設計・デザインのいろいろな部分を、既に書いたコンポーネントを再利用することで構築できるようになり、開発速度がアップすることに気付くでしょう。上記のような目次を、<TableOfContents /> を書くことでどのページにでも加えることができるのです! Chakra UI や Material UI のような、React オープンソースコミュニティーで共有されている何千ものコンポーネントを使い、プロジェクトを一気にスタートさせることも可能です。
伝統的なウェブページの作成方法とは、ウェブ開発者が先にコンテンツをマークアップしてから、ユーザとのインタラクションを加えるために JavaScript をちょっと添える、というものでした。これはウェブにとってインタラクションが「あると嬉しい」レベルのものだった時代にはうまく機能していました。しかし今や、インタラクションはほぼすべてのサイトとあらゆるアプリで必要とされるものです。React は同じテクノロジを使っていますが、インタラクティビティ・ファーストになっています。すなわち React コンポーネントとは、マークアップを添えることができる JavaScript 関数です。コンポーネントは以下のような見た目をしています(以下のコードは編集できます)。
export default function Profile() {
return (
<img
src="https://react.dev/images/docs/scientists/MK3eW3Am.jpg"
alt="Katherine Johnson"
/>
)
}img { height: 200px; }コンポーネントは以下のようにして作成します。
頭にある export default は標準的な JavaScript の構文です(React 特有のものではありません)。これでファイル内のメインの関数をマークし、後で他のファイルからそれをインポートできるようにします。(コンポーネントのインポートとエクスポートに詳細があります!)
function Profile() { } のように書くことで、Profile という名前の JavaScript 関数を定義します。
React コンポーネントは普通の JavaScript 関数ですが、名前は大文字から始める必要があります。さもないと動作しません!
このコンポーネントは src と alt という属性を有する <img /> タグを返しています。<img /> はまるで HTML のように書かれていますが、裏では実際には JavaScript です! この構文は JSX と呼ばれるもので、これによりマークアップを JavaScript 内に埋め込めるようになります。
return 文は、以下のように 1 行にまとめて書いても構いません。
return <img src="https://react.dev/images/docs/scientists/MK3eW3As.jpg" alt="Katherine Johnson" />;しかし return キーワードと同じ行にマークアップ全体が収まらない場合は、括弧で囲んで以下のようにする必要があります。
return (
<div>
<img src="https://react.dev/images/docs/scientists/MK3eW3As.jpg" alt="Katherine Johnson" />
</div>
);括弧がないと、return の後にあるコードはすべて無視されてしまいます!
Profile コンポーネントが定義できたので、それをほかのコンポーネント内にネストさせることができます。例えば Profile コンポーネントを複数回使う Gallery というコンポーネントをエクスポートできます。
function Profile() {
return (
<img
src="https://react.dev/images/docs/scientists/MK3eW3As.jpg"
alt="Katherine Johnson"
/>
);
}
export default function Gallery() {
return (
<section>
<h1>Amazing scientists</h1>
<Profile />
<Profile />
<Profile />
</section>
);
}img { margin: 0 10px 10px 0; height: 90px; }大文字・小文字の違いに気をつけてください。
<section>は小文字なので、React はこれが HTML タグを指しているのだと理解します。<Profile />は大文字のPで始まっているので、React はProfileという名前の独自コンポーネントを使いたいのだと理解します。
Profile の中には <img /> という HTML が更に含まれてます。最終的に、ブラウザに見えるのは以下のようなものです。
<section>
<h1>Amazing scientists</h1>
<img src="https://react.dev/images/docs/scientists/MK3eW3As.jpg" alt="Katherine Johnson" />
<img src="https://react.dev/images/docs/scientists/MK3eW3As.jpg" alt="Katherine Johnson" />
<img src="https://react.dev/images/docs/scientists/MK3eW3As.jpg" alt="Katherine Johnson" />
</section>コンポーネントは普通の JavaScript 関数ですので、同じファイルに複数のコンポーネントを書いておくこともできます。これはコンポーネントが比較的小さい場合や互いに密接に関連している場合には便利です。ファイルの中身が増えてきたら、いつでも Profile を別のファイルに移動できます。このやり方についてはすぐ後で、インポートについてのページで学びます。
Profile コンポーネントは Gallery コンポーネントの中でレンダーされています(しかも何回も)ので、Gallery は親コンポーネントであり、Profile を「子」としてレンダーしている、と言うことができます。これが React の魔法です。一度コンポーネントを定義したら、それをどこにでも何回でも好きなだけ使えるということです。
コンポーネントがほかのコンポーネントをレンダーすることはできますが、コンポーネントの定義をネストさせてはいけません。
export default function Gallery() {
// 🔴 Never define a component inside another component!
function Profile() {
// ...
}
// ...
}上記のコードはとても遅く、バグの原因になります。代わりに、すべてのコンポーネントをトップレベルで定義するようにしてください。
export default function Gallery() {
// ...
}
// ✅ Declare components at the top level
function Profile() {
// ...
}子コンポーネントが親コンポーネントの情報を必要とする場合は、コンポーネント定義をネストさせるのではなく props を通じて渡すようにしてください。
React アプリケーションは "ルート (root)" コンポーネントから始まります。通常、これは新しいプロジェクトを開始したときに自動的に作成されます。例えば CodeSandbox を使う場合や、Next.js のようなフレームワークを使う場合、ルートコンポーネントは pages/index.js に定義されています。ここまでの例でも、ルートコンポーネントをエクスポートしていたわけです。
ほとんどの React アプリでは隅から隅までコンポーネントが使われます。つまり、ボタンのような再利用可能なところでのみ使うのではなく、サイドバーやリスト、最終的にはページ本体といった大きなパーツのためにも使うのです。コンポーネントは、1 回しか使わないような UI コードやマークアップであっても、それらを整理するための有用な手段です。
React ベースのフレームワークではこれを更に 1 歩押し進めます。空の HTML ファイルから始めて JavaScript で React にページ内容の管理を引き継がせるのではなく、あなたの書いた React コンポーネントから HTML ファイル自体も自動生成するのです。これにより、JavaScript コードがロードされる前にコンテンツの一部をアプリが表示できるようになります。
その一方で、多くのウェブサイトでは React を "対話機能をちょっと添える" ためにのみ使っています。そのようなサイトはページ全体のためのルートコンポーネントを 1 つだけ持つのではなく、たくさんのルートコンポーネントを持っています。必要しだいで、React を使う量は多くても少なくても構わないのです!
これで初めての React 体験は完了です。キーポイントをいくつかおさらいしておきましょう。
-
React によりアプリのための再利用可能な部品であるコンポーネントを作成できる。
-
React アプリでは UI のあらゆる部品はコンポーネントである。
-
React のコンポーネントとは普通の JavaScript 関数だが、以下の点が異なる。
- 名前は常に大文字で始まる。
- JSX マークアップを return する。
このサンドボックスはルートコンポーネントがエクスポートされていないため動作しません:
function Profile() {
return (
<img
src="https://react.dev/images/docs/scientists/lICfvbD.jpg"
alt="Aklilu Lemma"
/>
);
}img { height: 181px; }答えを見る前に自分で修正してみましょう!
以下のように関数の前に export default を付けてください:
export default function Profile() {
return (
<img
src="https://react.dev/images/docs/scientists/lICfvbD.jpg"
alt="Aklilu Lemma"
/>
);
}img { height: 181px; }これを修正するのに export とだけ書いたのではなぜ不十分なのか気になるかもしれません。export と export default の違いについてはコンポーネントのインポートとエクスポートで学ぶことができます。
この return 文はどうもおかしいようです。直せますか?
もしかしたら修正途中で "Unexpected token" というエラーが出るかもしれません。その場合はセミコロンが閉じ括弧の後にあることを確認してください。return ( ) の中にセミコロンが残っているとエラーになります。
export default function Profile() {
return
<img src="https://react.dev/images/docs/scientists/jA8hHMpm.jpg" alt="Katsuko Saruhashi" />;
}img { height: 180px; }このコンポーネントを修正するには、以下のように return 文を 1 行にします:
export default function Profile() {
return <img src="https://react.dev/images/docs/scientists/jA8hHMpm.jpg" alt="Katsuko Saruhashi" />;
}img { height: 180px; }または、return する JSX マークアップを return の直後から括弧で囲んでも構いません:
export default function Profile() {
return (
<img
src="https://react.dev/images/docs/scientists/jA8hHMpm.jpg"
alt="Katsuko Saruhashi"
/>
);
}img { height: 180px; }この Profile の宣言のしかたや使われ方は何か間違っています。間違いがどこか分かりますか?(React がどのようにしてコンポーネントと普通の HTML タグを区別するのか思い出してみましょう!)
function profile() {
return (
<img
src="https://react.dev/images/docs/scientists/QIrZWGIs.jpg"
alt="Alan L. Hart"
/>
);
}
export default function Gallery() {
return (
<section>
<h1>Amazing scientists</h1>
<profile />
<profile />
<profile />
</section>
);
}img { margin: 0 10px 10px 0; height: 90px; }React コンポーネントの名前は大文字で始めなければなりません。
function profile() を function Profile() に、そして <profile /> をすべて <Profile /> に書きかえましょう:
function Profile() {
return (
<img
src="https://react.dev/images/docs/scientists/QIrZWGIs.jpg"
alt="Alan L. Hart"
/>
);
}
export default function Gallery() {
return (
<section>
<h1>Amazing scientists</h1>
<Profile />
<Profile />
<Profile />
</section>
);
}img { margin: 0 10px 10px 0; }ゼロからコンポーネントを書いてください。有効な名前ならどんな名前でも構いませんし、どんなマークアップを返しても構いません。何も思いつかないなら <h1>Good job!</h1> と表示する Congratulations というコンポーネントを書いてみましょう。エクスポートするのを忘れずに!
// Write your component below!export default function Congratulations() {
return (
<h1>Good job!</h1>
);
}