JSX の基本
JSX は React で UI を記述するための構文拡張です。 HTML に似た見た目ですが、実際には JavaScript の構文糖衣(シンタックスシュガー)です。 この章では、JSX の書き方、ルール、式の埋め込み、コンパイルの仕組みを学びます。
JSX とは
JSX(JavaScript XML) は、JavaScript の中に HTML のようなマークアップを書くための構文拡張です。
React コンポーネントの return 文の中で使われ、
どのような UI をレンダリングするかを直感的に記述できます。
function Greeting() {
const name = 'React';
return (
<div>
<h1>こんにちは、{name}!</h1>
<p>JSX を使って UI を構築しています。</p>
</div>
);
}
JSX は HTML ではない
JSX は見た目こそ HTML に似ていますが、実際には React.createElement() 関数の呼び出しに変換される JavaScript の式です。
ブラウザが直接 JSX を理解することはできないため、ビルド時に Vite(内部で Babel / SWC を使用)が
JSX を通常の JavaScript に変換します。
// 開発者が書く JSX
const element = <h1 className="title">Hello</h1>;
// ビルドツールが変換した結果(実際にブラウザで実行される)
const element = React.createElement(
'h1',
{ className: 'title' },
'Hello'
);
React 17 以降では、新しい JSX トランスフォームにより import React from 'react' を
各ファイルに書く必要がなくなりました。Vite のテンプレートではこの新しいトランスフォームが
デフォルトで有効になっています。
JSX のルール
JSX には HTML とは異なるいくつかの重要なルールがあります。
ルール 1: 単一のルート要素を返す
JSX では、コンポーネントが返す要素は必ず 1 つのルート要素で囲む必要があります。 複数の要素を並列で返すことはできません。
// エラーになる!
function BadComponent() {
return (
<h1>タイトル</h1>
<p>本文</p>
);
}
function GoodComponent() {
return (
<div>
<h1>タイトル</h1>
<p>本文</p>
</div>
);
}
function GoodComponent() {
return (
<>
<h1>タイトル</h1>
<p>本文</p>
</>
);
}
ルール 2: すべてのタグを閉じる
HTML では <img> や <br> などの自己閉じタグを閉じなくても動作しますが、
JSX ではすべてのタグを明示的に閉じる必要があります。
<!-- HTML: 閉じなくても OK -->
<img src="photo.jpg">
<br>
<input type="text">
{/* JSX: 必ず閉じる */}
<img src="photo.jpg" />
<br />
<input type="text" />
ルール 3: camelCase で属性を書く
JSX は JavaScript に変換されるため、HTML の属性名は JavaScript のオブジェクトプロパティになります。 JavaScript の予約語と衝突する属性名や、ハイフン区切りの属性名は camelCase に変更されています。
| HTML の属性 | JSX の属性 | 理由 |
|---|---|---|
class |
className |
class は JavaScript の予約語 |
for |
htmlFor |
for は JavaScript の予約語 |
tabindex |
tabIndex |
camelCase に統一 |
onclick |
onClick |
camelCase に統一 |
maxlength |
maxLength |
camelCase に統一 |
readonly |
readOnly |
camelCase に統一 |
最もよくあるミスが class と className の混同です。
class と書いてもエラーにはなりませんが、コンソールに警告が表示されます。
常に className を使いましょう。
式の埋め込み
JSX の中で 波括弧 {} を使うと、JavaScript の式を埋め込むことができます。
変数の値、計算結果、関数の戻り値など、値を返すものなら何でも埋め込めます。
function UserProfile() {
const name = '田中太郎';
const age = 25;
const hobbies = ['読書', 'プログラミング', 'ドラム'];
return (
<div>
{/* 変数の埋め込み */}
<h2>{name}</h2>
{/* 計算式の埋め込み */}
<p>来年の年齢: {age + 1} 歳</p>
{/* メソッドの戻り値 */}
<p>趣味: {hobbies.join('、')}</p>
{/* テンプレートリテラルも使える */}
<p>{`${name} さんは ${age} 歳です`}</p>
{/* 関数の呼び出し */}
<p>現在時刻: {new Date().toLocaleTimeString()}</p>
</div>
);
}
埋め込めるもの・埋め込めないもの
| 埋め込み可能 | 例 |
|---|---|
| 文字列・数値 | {'Hello'} / {42} |
| 変数 | {name} |
| 計算式 | {price * 1.1} |
| 関数の呼び出し | {formatDate(today)} |
| 三項演算子 | {isAdmin ? '管理者' : 'ユーザー'} |
| 配列の map | {items.map(item => <li>{item}</li>)} |
| 埋め込み不可 | 理由 |
|---|---|
オブジェクト({{}}) |
オブジェクトはそのまま表示できない([object Object] になる) |
if 文 |
if は文であり式ではない。三項演算子を使う |
for 文 |
for は文であり式ではない。map() を使う |
式(expression)と文(statement)の違いを覚えておきましょう。
式は値を返すもの(1 + 2、name、a ? b : c)で、
文は値を返さない命令(if、for、const x = 1)です。
JSX の {} 内には式しか書けません。
条件付き表示
JSX の中で条件によって表示を切り替えたい場合、if 文は使えないため、
三項演算子や && 演算子を使います。
三項演算子(2 パターンの切り替え)
function UserStatus({ isLoggedIn }) {
return (
<div>
{isLoggedIn
? <p>ようこそ!</p>
: <p>ログインしてください。</p>
}
</div>
);
}
&& 演算子(表示 / 非表示の切り替え)
条件が true の時だけ表示し、false の時は何も表示しない場合は
&& 演算子が便利です。
function Notification({ messages }) {
return (
<div>
<h2>通知</h2>
{/* messages が 0 件でなければ表示 */}
{messages.length > 0 && (
<p>未読メッセージが {messages.length} 件あります。</p>
)}
</div>
);
}
&& の左辺に数値 0 を使わないように注意してください。
{count && <p>表示</p>} と書くと、count が 0 の時に
「何も表示しない」のではなく 0 という数字がレンダリングされます。
{count > 0 && <p>表示</p>} のように比較演算子を使いましょう。
スタイルの適用
className の使い方
JSX では CSS クラスを className 属性で指定します。
外部 CSS ファイルにスタイルを定義し、コンポーネントで import して使うのが基本的なパターンです。
/* CSS ファイル */
.btn {
padding: 8px 16px;
border-radius: 4px;
border: none;
cursor: pointer;
}
.btn-primary {
background-color: #0d6efd;
color: white;
}
.btn-danger {
background-color: #dc3545;
color: white;
}
import './Button.css';
function Button({ variant, children }) {
return (
<button className={`btn btn-${variant}`}>
{children}
</button>
);
}
// 使用例
<Button variant="primary">送信</Button>
<Button variant="danger">削除</Button>
インラインスタイル
JSX でインラインスタイルを指定する場合は、文字列ではなく JavaScript オブジェクト を渡します。 プロパティ名は CSS のハイフン区切りではなく camelCase で書きます。
// HTML のインラインスタイル
// <div style="background-color: blue; font-size: 16px;">
// JSX のインラインスタイル(オブジェクトで渡す)
const style = {
backgroundColor: 'blue', // camelCase
fontSize: '16px', // camelCase
padding: '10px 20px',
};
<div style={style}>スタイル適用</div>
// 直接オブジェクトを渡す場合(二重波括弧に注意)
<div style={{ color: 'red', fontWeight: 'bold' }}>
赤い太字
</div>
二重波括弧 {{}} は特別な構文ではありません。
外側の {} が「JSX で JavaScript の式を埋め込む」ための記号で、
内側の {} が「JavaScript のオブジェクトリテラル」です。
インラインスタイルは動的にスタイルを変更したい場合に便利ですが、
基本的には CSS ファイル(または CSS Modules)を使うことを推奨します。
インラインスタイルは疑似クラス(:hover 等)やメディアクエリが使えないためです。
JSX のコンパイル
JSX はブラウザが直接理解できないため、ビルドツール(Vite / Babel / SWC)によって 通常の JavaScript に変換(トランスパイル)されます。 変換の仕組みを理解しておくと、JSX のルールがなぜ存在するのかが分かります。
// ======= 開発者が書く JSX =======
function Card() {
return (
<div className="card">
<h2>タイトル</h2>
<p>本文テキスト</p>
</div>
);
}
// ======= 変換後の JavaScript =======
function Card() {
return React.createElement(
'div',
{ className: 'card' },
React.createElement('h2', null, 'タイトル'),
React.createElement('p', null, '本文テキスト')
);
}
この変換の仕組みから、JSX のルールの理由が理解できます。
| ルール | 理由 |
|---|---|
| 単一のルート要素 | return 文は 1 つの値しか返せないため、1 つの createElement 呼び出しに変換する必要がある |
| タグを閉じる | JSX パーサーがタグの開始と終了を正しく解析するために必要 |
| camelCase 属性 | 属性は JavaScript オブジェクトのプロパティになるため、JavaScript の命名規則に従う |
フラグメント
フラグメント(Fragment) は、余分な DOM ノードを追加せずに 複数の要素をグループ化するための仕組みです。
import { Fragment } from 'react';
function UserInfo() {
return (
<Fragment>
<dt>名前</dt>
<dd>田中太郎</dd>
<dt>年齢</dt>
<dd>25 歳</dd>
</Fragment>
);
}
function UserInfo() {
return (
<>
<dt>名前</dt>
<dd>田中太郎</dd>
<dt>年齢</dt>
<dd>25 歳</dd>
</>
);
}
フラグメントは以下のような場面で役立ちます。
<table>の中に<tr>をグループ化したい(<div>で囲むと不正な HTML になる)<dl>の中に<dt>と<dd>のペアをグループ化したい- 不要な DOM 階層を増やしたくない
省略記法 <></> では key 属性を指定できません。
リストのレンダリングで key が必要な場合は、正式な <Fragment key={id}> を使用してください。
以上が JSX の基本ルールです。次の章では、React の核心であるコンポーネントについて詳しく学びます。