Skip to content

3.2 JSX の本質 — 関数呼び出しの糖衣 ​

ここで押さえてほしいのは、「JSX は HTML ではなく、JavaScript の関数呼び出しを書きやすくするための 構文拡張 である」という一点です。見た目は HTML にそっくりですが、実体は React.createElement(...) の呼び出しなんですね。

最初は「HTML を書いているつもり」でも進めますが、少しつまずいた時にこの事実を思い出すとだいぶ楽になります。

JSX は何者か ​

React の公式ドキュメントは JSX を次のように定義しています。

JSX is a syntax extension for JavaScript that lets you write HTML-like markup inside a JavaScript file.

出典: react.dev — Writing Markup with JSX

つまり JSX は「JavaScript の中に HTML っぽい書き方を混ぜていいよ」という構文上の許可証です。ブラウザは JSX を理解しないので、ビルドツール (Part 3.1 の Vite) が JavaScript の関数呼び出しに変換してからブラウザに送ります。

公式はさらにこうも言っています。

JSX and React are two separate things. They're often used together, but you can use them independently of each other.

出典: 同上

JSX は構文ルールで、React はライブラリです。二つを結びつけているのは、JSX が変換されると React.createElement(...) (あるいは新しい transform では react/jsx-runtime の関数) を呼び出すコードになる、というただの約束だけです。

createElement への変換 ​

次の JSX を見てください。

tsx
const element = <h1 className="title">Hello</h1>

これを Vite (内部の esbuild / Babel) が変換すると、概ね次のコードになります。

ts
// 変換後 (概念的なイメージ)
const element = React.createElement('h1', { className: 'title' }, 'Hello')

createElement のシグネチャは公式に明記されています。

js
const element = createElement(type, props, ...children)

出典: react.dev — createElement Reference

  • type — タグ名の文字列 ('h1') またはコンポーネント関数
  • props — 属性のオブジェクト (null 可)
  • ...children — 0 個以上の子要素

戻り値は「React 要素オブジェクト」です。ただの JavaScript オブジェクトであって、DOM そのものではありません。React は後でこのオブジェクトを見て「ブラウザの DOM をどう変更するか」を決めます。

図 3.2.1 JSX からブラウザへの変換フロー ​

JSX ↔ createElement を切り替えて見てみる ​

文章で「変換される」と言われても、最初はピンと来ないですよね。下のデモは、JSX のいくつかのパターンを選ぶと、変換後の createElement 呼び出しが隣に並んで出る、というものです。タブを切り替えて、左右を見比べてみてください。

JSX書くもの
const el = <h1 className="title">Hello</h1>
JS実際に動くもの
const el = React.createElement(
  'h1',
  { className: 'title' },
  'Hello'
)

タグ名は文字列に、属性はオブジェクトに、子要素は第 3 引数以降に並びます。

見るポイントは 2 つです。

  • タグ名の文字列化 — <h1> は 'h1' という文字列に、<Greeting> (大文字始まり) は Greeting という関数そのものになる
  • 子が「第 3 引数以降」として並ぶ — 入れ子の JSX は、そのまま引数リストとしてフラットに並ぶ

この見方が染みつくと、「JSX は見た目こそ HTML だけど、書いているのは関数呼び出し」という感覚が馴染んできます。

HTML との違い ​

JSX は HTML に似ていますが、同一ではありません。公式がまとめている 3 つのルールを押さえておきましょう。

ルール 1 — ルート要素はひとつ ​

コンポーネントは複数の要素をそのままは返せません。ひとつの親タグで包むか、Fragment (<>...</>) を使います。

tsx
// NG: 兄弟をそのまま返している
function Bad() {
  return (
    <h1>タイトル</h1>
    <p>本文</p>
  )
}

// OK: Fragment で包む
function Good() {
  return (
    <>
      <h1>タイトル</h1>
      <p>本文</p>
    </>
  )
}

Fragment は DOM に余計な要素を足さないので、<div> のラッパが不要な場面で便利です。

ルール 2 — すべてのタグを閉じる ​

HTML では許されていた <img> や <br> のような省略形は、JSX では使えません。

tsx
<img src="/logo.png" alt="ロゴ" />
<br />

ルール 3 — 属性は camelCase、予約語は言い換え ​

HTML の属性は JSX では主に camelCase になります。さらに JavaScript の予約語とぶつかる class / for は別名になっています。

HTMLJSX
class="x"className="x"
for="id"htmlFor="id"
tabindex="0"tabIndex={0}
onclick="..."onClick={handler}

aria-* と data-* は歴史的事情から、HTML と同じくハイフン区切りのままです (公式が明記しています)。

{式} で JavaScript を埋め込む ​

JSX の中で { } を書くと、中身が JavaScript 式として評価されます。公式はこう説明しています。

You can only use curly braces in two ways inside JSX:

  1. As text directly inside a JSX tag
  2. As attributes immediately following the = sign

出典: react.dev — JavaScript in JSX with Curly Braces

tsx
const name = 'React'
const avatar = '/me.png'

function Greeting() {
  return (
    <div>
      <h1>こんにちは、{name} さん</h1>
      <img src={avatar} alt={name} />
    </div>
  )
}

ひとつ注意として、src="{avatar}" のようにクォートで囲むと、ただの文字列 "{avatar}" として扱われてしまいます。

オブジェクトを渡すときの二重中括弧 ​

style 属性にオブジェクトを渡すときは、下のコードのように中括弧が 2 重になります。外側が JSX の式の始まり、内側がオブジェクトリテラル、というだけのことです。

tsx
<p style={{ color: 'red', fontSize: 16 }}>警告</p>

CSS プロパティも camelCase (font-size → fontSize) です。

条件レンダリングとリスト ​

{ } の中に書けるのは式なので、三項演算子や && や map がそのまま使えます。

tsx
type Props = { isLoggedIn: boolean; todos: string[] }

function Panel({ isLoggedIn, todos }: Props) {
  return (
    <section>
      {isLoggedIn ? <span>ようこそ</span> : <a href="/login">ログイン</a>}

      {todos.length > 0 && (
        <ul>
          {todos.map((todo, i) => (
            <li key={i}>{todo}</li>
          ))}
        </ul>
      )}
    </section>
  )
}

key の意味は 3.7 リストと key で詳しく扱います。

手を動かして確かめる ​

以下は JSX の基本 (属性、{}、条件分岐、Fragment) を詰め込んだサンプルです。StackBlitz で編集すると、すぐに結果が反映されます。

StackBlitz で開く

まとめ — JSX は「関数呼び出しの見やすい書き方」 ​

  • JSX は HTML ではなく JavaScript の拡張構文で、ビルド時に createElement 呼び出しに変換される。
  • 戻り値は React 要素オブジェクト。DOM そのものではない。
  • HTML との差分 (閉じタグ必須、camelCase 属性、className / htmlFor) は「ブラウザではなく JavaScript の世界のため」の調整。
  • { } で式を埋め込める。二重中括弧は「外側が式、内側がオブジェクトリテラル」というだけ。

さらに深く学ぶには ​

参照ソース ​