目次
3 章

JSX の本質

JSX とは何で、どのように JavaScript の関数呼び出しに変換されるのか、そして式・属性・子ノードのルールを一気に扱います。

2 章で Vite で最初のプロジェクトを作りながら、自然と次のようなコードを見ました。

src/App.jsx
function App() {
  return (
    <div>
      <h1>こんにちはReact!</h1>
    </div>
  );
}

関数の中で HTML のようなコードをそのまま return しています。JavaScript の中に HTML だなんて、どこか不自然ではないでしょうか。本章ではこの構文 — JSX — が正確には何で、どのように動作するのか、そして覚えておくべき基本ルールを見ていきます。

JSX とは何か #

JSX(JavaScript XML) は JavaScript に HTML に似たマークアップ構文を合わせたものです。JavaScript の公式構文ではありませんが、Facebook が React を発表したときに一緒に提案した拡張構文で、今日ではほぼすべての React プロジェクトが JSX を使います。

JSX なしでも React コードを書けます。次の 2 つのコードはまったく同じ結果になります。

JSX を使う
function App() {
  return <h1>Hello</h1>;
}
JSX なし
function App() {
  return React.createElement('h1', null, 'Hello');
}

上の方がはるかに読みやすいです。だからすべての React プロジェクトが JSX を使います。

ブラウザは JSX を知らない #

ブラウザの JavaScript エンジンは JSX を直接理解しません。私たちが JSX で書いたコードは、ビルド工程で普通の JavaScript に変換されてからブラウザに渡されます。2 章でインストールした Vite がこの変換を自動で処理してくれるので、私たちは気にせず JSX を思い切り使えます。

変換の大まかな流れは次のとおりです。

JSX → JS 変換の流れ
src/App.jsx (JSX)
   ↓ Vite (esbuild / SWC)
JS bundle (React.createElement 呼び出し)
   ↓ ブラウザ
DOM 要素

8 章で扱う key、14 章で扱う reconciliation のすべてが、この React.createElement 呼び出しで作られたオブジェクトツリーの上で動作します。本章ではその一段上の構文ルールに集中します。

JSX の基本ルール #

JSX は HTML に似ていますが、いくつか異なるルールがあります。初めて学ぶときに最も間違えやすい部分なので、順番に押さえていきます。

ルール 1. 1 つの親要素で包む #

JSX は結局のところ関数が何かを return するものなので、一度に 1 つの値しか返せません。複数の要素を一度に返すには、必ず 1 つの親で包む必要があります。

悪い例
function App() {
  return (
    <h1>こんにちは</h1>
    <p>今日もよい一日を</p>
  );
}
よい例
function App() {
  return (
    <div>
      <h1>こんにちは</h1>
      <p>今日もよい一日を</p>
    </div>
  );
}

<div> で包んでも構いませんが、意味のない <div> が増えていくのが嫌な場面もあります。そんなときは空タグである Fragment を使います。

Fragment を使う
function App() {
  return (
    <>
      <h1>こんにちは</h1>
      <p>今日もよい一日を</p>
    </>
  );
}

<>...</>React.Fragment の短い表記です。実際の DOM にはどんな要素も作らずに、複数の子をまとめる役割だけを担います。

ルール 2. すべてのタグは閉じる #

HTML では <img><br><input> のような一部のタグを閉じずに書くこともできました。しかし JSX ではすべてのタグを閉じる必要があります。子のないタグは末尾に / を付けて自己閉じ(self-closing)にします。

JSX
<img src="/cat.png" alt="ねこ" />
<br />
<input type="text" />

ルール 3. class の代わりに className #

HTML で CSS クラスを指定するときに使う class 属性は JavaScript の予約語と被るので、JSX では className を使います。

JSX
<div className="card">カードです</div>

同じ理由で <label>for 属性も htmlFor に変わります。

JSX
<label htmlFor="email">メール</label>
<input id="email" type="email" />

ルール 4. 属性名は camelCase #

HTML では属性名がすべて小文字でしたが(onclicktabindexreadonly)、JSX では camelCase を使います。

JSX
<button onClick={handleClick} tabIndex={0}>クリック</button>
<input readOnly value="固定値" />

このルールは 7 章(イベントハンドリング)で本格的に再登場します。onClickonChangeonSubmit すべて camelCase です。

JSX の中に JavaScript の式を入れる #

JSX の本当の強みは、マークアップの中に JavaScript の式を自由に差し込めることです。波括弧 { } の中に入るすべての JavaScript の式は評価され、その結果が画面に出力されます。

src/App.jsx
function App() {
  const name = '太郎';
  const age = 30;

  return (
    <div>
      <h1>こんにちは{name}さん!</h1>
      <p>年齢: {age}</p>
      <p>10 年後には: {age + 10}</p>
    </div>
  );
}

関数呼び出し、三項演算子、配列のメソッドも、式であればすべて使えます。

src/App.jsx
function App() {
  const isLoggedIn = true;
  const items = ['りんご', 'バナナ', 'チェリー'];

  return (
    <div>
      <h1>{isLoggedIn ? 'ようこそ!' : 'ログインしてください'}</h1>
      <ul>
        {items.map(item => <li key={item}>{item}</li>)}
      </ul>
    </div>
  );
}
注記
波括弧の中には**式(expression)だけが入ります。if 文や for ループのような文(statement)**は直接入れられません。条件付き出力には三項演算子や && 演算子を、繰り返し出力には map メソッドを使うといった具合です。詳しくは 7 章 条件付きレンダリングと 8 章 リストと key で扱います。

インラインスタイル #

JSX で直接スタイルを指定するには、オブジェクトの形で渡します。CSS プロパティ名も同じく camelCase です。

src/App.jsx
function App() {
  return (
    <h1 style={{ color: 'tomato', fontSize: '32px' }}>
      スタイルが適用されたタイトル
    </h1>
  );
}

波括弧が 2 つあるのが最初はぎこちなく見えるかもしれませんが、外側の { } は「JSX の中の JavaScript の式」を意味し、内側の { } は「JavaScript のオブジェクトリテラル」だからです。

実務ではインラインスタイルの代わりに CSS Modules / Tailwind / styled-components のような道具を使うことが多いです。本書は例の読みやすさのためにインラインスタイルを使うこともありますが、デザインシステム次元のスタイリングは別の本で扱います。

JSX の中のコメント #

JSX の中でコメントを書くには、JavaScript の式なので波括弧で包む必要があります。

src/App.jsx
function App() {
  return (
    <div>
      {/* これは JSX コメントです */}
      <h1>タイトル</h1>
    </div>
  );
}

自分でやってみる #

2 章で作ったプロジェクトの src/App.jsx を次のように変えてみてください。

src/App.jsx
function App() {
  const name = '太郎';
  const fruits = ['りんご', 'バナナ', 'チェリー'];

  return (
    <>
      <h1>こんにちは{name}さん!</h1>
      <p>今日の果物は全部で {fruits.length} 個です:</p>
      <ul>
        {fruits.map(fruit => <li key={fruit}>{fruit}</li>)}
      </ul>
    </>
  );
}

export default App;

保存すると HMR で即座に画面が更新されます。変数の値を変えたり、配列に新しい果物を追加したりしながら、画面がどう反応するか確認してみてください。

ヒント
JSX コードを書いていてビルドエラーが出たら、十中八九は上で扱った 4 つのルールのどれかをうっかり忘れた場合です。「複数の要素を親で包んだか?」「すべてのタグを閉じたか?」「classclassName と書いたか?」「属性を camelCase で書いたか?」の順にチェックすれば、ほとんど解決します。

練習問題 #

  1. 上の「自分でやってみる」コードの fruits 配列に自分の好きな果物を 3 つ追加し、保存直後に画面に 6 個の項目すべてが描かれるか確認してみてください。その後、<p>全部で N 個 の部分も自動的に 6 に変わるか確認します。
  2. <h1> のスタイルをインラインスタイルに変えて、色を tomato、フォントサイズを 48px にしてみてください。style={{ color: 'tomato', fontSize: '48px' }} のようにオブジェクトの形で渡します。
  3. fruits 配列をオブジェクトの配列に変えてみてください。各項目が { id, name, emoji } の形のオブジェクトで、<li> の中では {fruit.emoji} {fruit.name} のように表示します。key には fruit.id を使います。オブジェクト配列の扱いは 8 章(リストと key)の土台になります。

一行まとめ: JSX は JavaScript の関数呼び出し(React.createElement)に変換される構文だ。親で包む / タグを閉じる / classNamehtmlFor / camelCase 属性 — この 4 つのルールを身につければよい。波括弧 { } で JSX の中に JavaScript の式を自由に差し込める。

次の章 #

次の第 4 章 コンポーネントと propsでは、上の例の App のような関数が実は React の最も重要な単位であるコンポーネントであることを押さえ、コンポーネント同士でデータをやり取りする通路である props を見ていきます。17 章(props と children の型付け)で TypeScript によってそのインターフェースを固める作業の土台になります。

X