React・まず動かしてみる

コンポーネントに切り分ける

Reactの部品は関数1つです。カードをBirdCardという部品に切り出し、別ファイルへ移して読みこむところまでやります。名前を大文字で始める理由や、1ファイル1コンポーネントの慣習も扱います。

前回、カードを1枚書きました。今回はそれを部品として切り出します

Reactでいう部品(コンポーネント)の正体は、JSXを返す関数です。それだけです。

function BirdCard() {
  return <div className="card">🐦 シマエナガ</div>
}

使うときは、自分で作ったHTMLタグのように書きます。

<BirdCard />

決まりは2つだけ

コンポーネントとして扱われるための条件は、たった2つです。

  1. 名前を大文字で始める
  2. JSXを返す

ファイルに切り出す

コンポーネントはApp.jsxの中に書いても動きますが、1つのファイルに1つの部品にしておくと、後から探しやすくなります。慣習としてsrc/components/にまとめます。

function BirdCard() {
  return (
    <div className="flex max-w-md gap-4 rounded-2xl bg-white p-5 shadow-sm">
      <span className="text-4xl">🐦</span>
      <div>
        <h2 className="font-bold text-slate-800">シマエナガ</h2>
        <p className="mt-1 text-sm text-slate-600">
          雪のようにまっ白な小鳥。北海道にすんでいる。
        </p>
      </div>
    </div>
  )
}

export default BirdCard

最後のexport default BirdCardが「このファイルの主役はこれです」という宣言です。これを書き忘れると、読みこむ側でundefinedになり「Element type is invalid」というエラーになります。

読みこむ側はこうです。

import BirdCard from './components/BirdCard.jsx'

function App() {
  return (
    <div className="min-h-screen bg-sky-50 p-8">
      <h1 className="text-2xl font-bold text-sky-800">しまちゃんのことりずかん</h1>
      <p className="mt-1 text-sm text-slate-500">全8羽をあつめよう</p>

      <div className="mt-6 space-y-4">
        <BirdCard />
        <BirdCard />
        <BirdCard />
      </div>
    </div>
  )
}

export default App

./から始まるパスは「このファイルと同じ場所から数えて」という意味です。App.jsxsrc/にあるので、./components/BirdCard.jsxsrc/components/BirdCard.jsxにたどりつきます。

べんりワザパスの読み方・書き方./../の数え方があやしい人はこちら。HTMLのときと考え方は同じです

<BirdCard /><BirdCard></BirdCard>

中身を持たない部品は、自分で閉じる書き方にします。

<BirdCard />

中に何か入れたいときは、開始タグと終了タグで挟みます。挟んだ中身はchildrenという名前で受け取れます(次回扱います)。

<Layout>
  <BirdCard />
</Layout>

部品にすると何がうれしいのか

上のコードでは<BirdCard />を3回書いています。3枚のカードが並びますが、中身は全部同じです。

もうひとつの利点はまとまりに名前が付くことです。App.jsxを開いたときに、

<Header />
<SearchBox />
<BirdList />

と並んでいれば、中身を読まなくても画面の構造が分かります。WordPressのテンプレートでget_header()get_template_part('parts/card')を並べるのと、読み心地は同じです。

どこまで細かく分けるか

最初から細かく割る必要はありません。名前が付けられるまとまりになったら切り出す——それで十分です。

切り出す価値があるまだ切り出さなくていい
カード・ヘッダー・検索ボックス(呼び名がある)<div>で囲っただけのまとまり
同じ形を何度も使う1回しか出てこない小さな飾り
中で状態を持つようになった数行のJSX

公式ドキュメントも見てみる

ja.react.dev外部サイト初めてのコンポーネント – React公式の「最初のコンポーネント」。「コンポーネントを定義する」の節に、大文字で始める理由とexport defaultの使い方が短くまとまっています

このレッスンのまとめ

  1. コンポーネントの正体はJSXを返す関数
  2. 名前は必ず大文字で始める——小文字だとHTMLタグ扱いになり、何も出ないまま静かに失敗する
  3. src/components/1ファイル1部品で置き、export defaultで書き出す
  4. 読みこむ側はimport BirdCard from './components/BirdCard.jsx'
  5. 部品にしただけでは同じ中身しか作れない——次回のpropsで1枚ずつ変える

やってみよう:カードをBirdCardに切り出す

前回App.jsxに直接書いたカードを、部品として独立させます。

  1. src/components/フォルダを作る
  2. その中にBirdCard.jsxを作り、上のコードを書く(末尾のexport defaultを忘れずに
  3. App.jsxの先頭でimportし、カードのJSXを<BirdCard />3つに置きかえる

この時点のフォルダはこうなります。

shima-zukan/
├── node_modules/
├── public/
├── src/
│   ├── components/
│   │   └── BirdCard.jsx   ← 今回作る
│   ├── App.jsx
│   ├── index.css
│   └── main.jsx
├── index.html
├── package.json
└── vite.config.js
水色の背景に、同じシマエナガのカードが縦に3枚並んでいる
まったく同じカードが3枚。物足りないのが正しい状態です

3枚とも同じ中身になっているのを確認してください。ここを次回、1枚ずつ違う鳥にします。

よくある質問

コンポーネントの名前を大文字で始めるのはなぜですか?
小文字で始まる名前は、Reactがhtmlのタグ(divやspan)として扱うためです。birdCardと書くと存在しないHTMLタグとみなされ、画面に何も出ません。エラーにならないので気づきにくい間違いです。
1つのファイルに複数のコンポーネントを書いてもいいですか?
動きます。ただし他のファイルから使う主役は1ファイルに1つ(export default)にしておくのが慣習です。そのファイルの中だけで使う小さな部品なら、同じファイルに置いて問題ありません。
コンポーネントはどれくらいの大きさで分けるべきですか?
「名前が付けられるまとまり」が目安です。カード・ヘッダー・検索ボックスのように呼び名があるものは部品にする価値があります。最初から細かく割らず、大きくなってきたら割るので十分です。