JSX と TSX

Part 1 · React の世界観

JSX と TSX

HTML に見えるが HTML ではない。その正体

React のコードを開くと、JavaScript の中に いきなり HTML のようなものが書いてあって面食らいます。

これは JSX と呼ばれる書き方で、HTML ではありません。 見た目が似ているだけで、正体は JavaScript です。

そこを取り違えると、「なぜ class ではなく className なのか」 「なぜ波括弧が必要なのか」がずっと腑に落ちないままになります。

JSX は JavaScript に変換される

JSX はそのままブラウザに届くわけではありません。 ビルドのときに、ふつうの JavaScript の関数呼び出しに書き換えられます。

// 書いているもの
<p className="text">こんにちは</p>

// 変換されたもの(イメージ)
jsx("p", { className: "text", children: "こんにちは" })

つまり JSX は「関数呼び出しの見た目を HTML 風にしたもの」です。 タグに見えているものは、実際には関数の引数です。

jsx-expression.tsx

const name = "さとう";
const price = 1200;
const tags = ["React", "TypeScript"];

export function JsxExpression() {
  return (
    <ul className="flex flex-col gap-2.5">
      {/* 変数をそのまま置ける */}
      <li className="rounded-md border p-2.5">{name} さん</li>

      {/* 計算もできる。式なら何でもよい */}
      <li className="rounded-md border p-2.5">{price * 1.1} 円(税込)</li>

      {/* メソッドの呼び出しも式 */}
      <li className="rounded-md border p-2.5">{name.length} 文字</li>

      {/* 条件による出し分けも、三項演算子なら式として書ける */}
      <li className="rounded-md border p-2.5">
        {tags.length > 0 ? tags.join(" / ") : "タグなし"}
      </li>
    </ul>
  );
}

波括弧で JavaScript に戻る

JSX の中で { } を書くと、そこだけ JavaScript に戻ります。 中には式なら何でも書けます。

いろいろな式を埋め込む

変数・計算・メソッド呼び出し・条件

  • さとう さん
  • 1320 円(税込)
  • 3 文字
  • React / TypeScript

ポイントは「式」しか書けないことです。 値になるものは書けますが、値にならないものは書けません。

{name}                       // OK(値になる)
{price * 1.1}                // OK
{isOpen ? "開" : "閉"}       // OK(三項演算子は式)

{if (isOpen) { ... }}        // NG(if は式ではない)
{for (...) { ... }}          // NG

iffor が書けないのは、それらが値にならないからです。 だから React では、条件分岐に三項演算子や && を使い、繰り返しに map を使います。どちらも式だからです。

jsx-expression.tsx

const name = "さとう";
const price = 1200;
const tags = ["React", "TypeScript"];

export function JsxExpression() {
  return (
    <ul className="flex flex-col gap-2.5">
      {/* 変数をそのまま置ける */}
      <li className="rounded-md border p-2.5">{name} さん</li>

      {/* 計算もできる。式なら何でもよい */}
      <li className="rounded-md border p-2.5">{price * 1.1} 円(税込)</li>

      {/* メソッドの呼び出しも式 */}
      <li className="rounded-md border p-2.5">{name.length} 文字</li>

      {/* 条件による出し分けも、三項演算子なら式として書ける */}
      <li className="rounded-md border p-2.5">
        {tags.length > 0 ? tags.join(" / ") : "タグなし"}
      </li>
    </ul>
  );
}

JSX は値なので、持ち回せる

JSX が値だということを、実際に確かめてみます。 変数に入れることも、配列に入れることもできます。

変数に入れる / 配列に入れる

badge を 2 か所で使い回している

  • さとう 新着
  • すずき 新着

map が JSX の配列を作っているのも、これと同じことです。 Part 0 でやった「map は新しい配列を返す」が、 そのまま「JSX の配列を返す」になっているだけです。

jsx-value.tsx

// JSX は値なので、変数に入れられる
const badge = <span className="rounded border px-2 py-0.5 text-xs">新着</span>;

// 配列に入れることもできる。これがリスト表示の正体
const rows = ["さとう", "すずき"].map((memberName) => (
  <li key={memberName} className="rounded-md border p-2.5">
    {memberName} {badge}
  </li>
));

export function JsxValue() {
  return <ul className="flex flex-col gap-2.5">{rows}</ul>;
}

HTML と違うところ

JSX は JavaScript なので、HTML の書き方がそのままでは使えない箇所があります。 数は多くありません。

class ではなく className

class は JavaScript の予約語(クラスを作るための言葉)なので、 属性名として使えません。そのため className になっています。 同じ理由で forhtmlFor です。

属性はキャメルケース

<input onChange={...} maxLength={10} tabIndex={0} />

onchange ではなく onChange。 JavaScript のオブジェクトのキーとして書かれるので、 JavaScript の命名にそろえてあります。

タグは必ず閉じる

<br />   // ○ 自分で閉じる
<br>     // ✕ エラーになる

返せるのはひとつだけ

関数が返せる値はひとつなので、JSX も一番外側はひとつでなければいけません。 ただ、そのために余計な div を増やしたくないときは、 名前のないタグで囲みます。

// 余計な div が増えてしまう
<div>
  <p>A</p>
  <p>B</p>
</div>

// 画面には何も出ない、まとめるためだけのタグ
<>
  <p>A</p>
  <p>B</p>
</>

<> </> は Fragment と呼ばれます。 「まとめたいだけで、要素は増やしたくない」ときに使います。

jsx-expression.tsx

const name = "さとう";
const price = 1200;
const tags = ["React", "TypeScript"];

export function JsxExpression() {
  return (
    <ul className="flex flex-col gap-2.5">
      {/* 変数をそのまま置ける */}
      <li className="rounded-md border p-2.5">{name} さん</li>

      {/* 計算もできる。式なら何でもよい */}
      <li className="rounded-md border p-2.5">{price * 1.1} 円(税込)</li>

      {/* メソッドの呼び出しも式 */}
      <li className="rounded-md border p-2.5">{name.length} 文字</li>

      {/* 条件による出し分けも、三項演算子なら式として書ける */}
      <li className="rounded-md border p-2.5">
        {tags.length > 0 ? tags.join(" / ") : "タグなし"}
      </li>
    </ul>
  );
}

理解できたか確かめる

確認クイズ

JSX の中で if 文が書けないのはなぜ?

確認クイズ

class ではなく className と書くのはなぜ?

jsx-expression.tsx

const name = "さとう";
const price = 1200;
const tags = ["React", "TypeScript"];

export function JsxExpression() {
  return (
    <ul className="flex flex-col gap-2.5">
      {/* 変数をそのまま置ける */}
      <li className="rounded-md border p-2.5">{name} さん</li>

      {/* 計算もできる。式なら何でもよい */}
      <li className="rounded-md border p-2.5">{price * 1.1} 円(税込)</li>

      {/* メソッドの呼び出しも式 */}
      <li className="rounded-md border p-2.5">{name.length} 文字</li>

      {/* 条件による出し分けも、三項演算子なら式として書ける */}
      <li className="rounded-md border p-2.5">
        {tags.length > 0 ? tags.join(" / ") : "タグなし"}
      </li>
    </ul>
  );
}

この章のまとめ

  • JSX は HTML ではなく、関数呼び出しに変換される JavaScript
  • 結果はなので、変数にも配列にも入れられる
  • { } の中にはしか書けない。 だから if ではなく三項演算子、for ではなく map
  • className やキャメルケースは、JavaScript の都合に合わせた結果
  • 一番外側はひとつだけ。増やしたくないときは <> </>

jsx-expression.tsx

const name = "さとう";
const price = 1200;
const tags = ["React", "TypeScript"];

export function JsxExpression() {
  return (
    <ul className="flex flex-col gap-2.5">
      {/* 変数をそのまま置ける */}
      <li className="rounded-md border p-2.5">{name} さん</li>

      {/* 計算もできる。式なら何でもよい */}
      <li className="rounded-md border p-2.5">{price * 1.1} 円(税込)</li>

      {/* メソッドの呼び出しも式 */}
      <li className="rounded-md border p-2.5">{name.length} 文字</li>

      {/* 条件による出し分けも、三項演算子なら式として書ける */}
      <li className="rounded-md border p-2.5">
        {tags.length > 0 ? tags.join(" / ") : "タグなし"}
      </li>
    </ul>
  );
}