JSXの基本
この章では、ReactのUIを記述するための構文であるJSXを学びます。
この章で学ぶこと
- JSXとは何か、なぜ使うのか
- JSXの4つの基本ルール
- JavaScriptの埋め込み方法
- インラインスタイルの適用方法
この章では03章で作成したプロジェクトを使用します。npm run devで開発サーバーを起動し、コードを書きながら学習を進めましょう。
Viteテンプレートのリセット
Viteのデフォルトテンプレートには、デモ用のスタイルが含まれています。これから自分でコードを書いていくため、シンプルな状態にリセットしましょう。
src/index.cssを以下の内容に置き換えてください。
:root {
font-family: system-ui, -apple-system, sans-serif;
line-height: 1.5;
}
body {
margin: 0;
padding: 20px;
}
button {
padding: 8px 16px;
font-size: 1rem;
cursor: pointer;
}
nav a {
margin-right: 16px;
}
src/App.cssを以下の内容に置き換えてください。
#root {
max-width: 800px;
margin: 0 auto;
text-align: center;
}
これでシンプルな状態になりました。テキストは中央寄せで表示され、ボタンの基本スタイルも用意されています。
本文中のコード例では、スタイルのインポート(import "./App.css")を省略している場合があります。演習の回答コードは完全な形で記載しているため、そのままApp.tsxにコピーして動作確認できます。
最初のJSX
src/App.tsxを開いてみましょう。Viteのテンプレートには最初から多くのコードがありますが、これをシンプルにしてJSXの基本を学びます。
src/App.tsxを以下のように書き換えてください。
function App() {
return <h1>Hello, React!</h1>
}
export default App
ブラウザを確認すると「Hello, React!」と表示されます。この<h1>Hello, React!</h1>がJSXです。
JSXとは
JSX(JavaScript XML)は、JavaScriptの中でHTMLライクな構文を記述できる拡張構文です。
const element = <h1>Hello, React!</h1>
JSXはブラウザが直接理解できないため、ビルド時にJavaScriptに変換されます。
JSXの内部変換
JSXはビルド時にreact/jsx-runtimeが提供するjsx()関数の呼び出しに変換されます(automatic runtime。React 17で導入され、Viteでも標準の方式です)。
// JSXで記述
const element = <h1 className="title">Hello</h1>
// 変換後のJavaScript(イメージ。importはビルド時に自動挿入される)
import { jsx as _jsx } from 'react/jsx-runtime'
const element = _jsx('h1', {
className: 'title',
children: 'Hello'
})
なお、従来の方式(classic transform)ではReact.createElement('h1', { className: 'title' }, 'Hello')に変換されていました。解説記事では今もこの形を見かけますが、どちらの方式でも「JSX 1つ = 関数呼び出し1つ」である点は同じです。この変換を理解すると、JSXの制約(単一ルート要素が必要など)の理由がわかります。
JSXの基本ルール
1. 単一のルート要素
JSXは必ず単一のルート要素を返す必要があります。
// OK: 単一のルート要素
const element = (
<div>
<h1>タイトル</h1>
<p>本文</p>
</div>
)
// NG: 複数のルート要素
const element = (
<h1>タイトル</h1>
<p>本文</p>
)
Fragment
余分なDOM要素を追加したくない場合は、Fragmentを使用します。
import { Fragment } from 'react'
const element = (
<Fragment>
<h1>タイトル</h1>
<p>本文</p>
</Fragment>
)
// 省略記法
const element = (
<>
<h1>タイトル</h1>
<p>本文</p>
</>
)
2. 閉じタグは必須
HTMLでは省略できる閉じタグも、JSXでは必須です。
// OK
<img src="image.png" alt="画像" />
<br />
<input type="text" />
// NG
<img src="image.png" alt="画像">
3. className属性
HTMLのclass属性は、JSXではclassNameを使用します。
<div className="container">
<p className="text-primary">テキスト</p>
</div>
classではなくclassNameを使う理由は、classがJavaScriptの予約語だからです。JSXはJavaScript内で動作するため、予約語との衝突を避ける必要があります。
4. キャメルケース
JSXの属性名はキャメルケースで記述します。
// HTMLの属性 → JSXの属性
// onclick → onClick
// tabindex → tabIndex
// for → htmlFor
<button onClick={handleClick}>クリック</button>
<label htmlFor="email">メール</label>
JavaScriptの埋め込み
JSX内では、波括弧{}を使ってJavaScript式を埋め込めます。※テンプレートリテラルや式については02章で解説しています。
変数の表示
const name = 'React'
const element = <h1>Hello, {name}!</h1>
式の評価
const element = <p>1 + 1 = {1 + 1}</p>
関数の呼び出し
const formatDate = (date: Date) => date.toLocaleDateString()
const element = <p>今日: {formatDate(new Date())}</p>
オブジェクトのプロパティ
const user = { name: '田中', age: 25 }
const element = <p>{user.name}さん({user.age}歳)</p>
スタイルの適用
インラインスタイル
インラインスタイルはオブジェクトで指定します。
const style = {
color: 'blue',
fontSize: '20px',
backgroundColor: '#f0f0f0'
}
const element = <p style={style}>スタイル付きテキスト</p>
// 直接記述する場合
const element = <p style={{ color: 'red', fontWeight: 'bold' }}>テキスト</p>
インラインスタイルではプロパティ名がキャメルケースになります(font-size → fontSize)。
コメント
JSX内でコメントを書く場合は、波括弧で囲みます。
const element = (
<div>
{/* これはコメントです */}
<p>テキスト</p>
</div>
)
試してみよう
以下の課題に挑戦してみましょう。
- classNameを試す:
<h1>にclassName="title"を追加し、どう表示されるか確認してみましょう - classを使ってみる:
classNameの代わりにclassを使うとどうなるか試してみましょう(警告を確認) - JavaScriptを埋め込む: 変数
nameを定義し、{name}で表示してみましょう - Fragmentを試す:
<>と</>で複数の要素を囲んでみましょう
ヒント
-
className="title"を追加するだけでOKです。CSSが定義されていなくても属性は適用されます。ブラウザの開発者ツールでHTML要素を確認してみましょう。 -
ブラウザのコンソール(F12キー → Console)を開いてから、
class属性を使ったコードを保存してください。警告メッセージが表示されます。なお、エディタにはTypeScriptの型エラー(赤線)が表示されますが、開発サーバーはそのまま実行できます。 -
関数内で
const name = 'React'のように変数を定義し、JSX内で{name}と記述すると変数の値が表示されます。 -
複数の要素を返したいときは
<>と</>で囲みます。例えば<h1>と<p>を並べて返すことができます。
回答と解説
課題1: classNameを試す
import './App.css'
function App() {
return <h1 className="title">Hello, React!</h1>
}
export default App
className属性を追加すると、HTMLではclass="title"として出力されます。ブラウザの開発者ツール(F12)でElements/要素タブを開くと、<h1 class="title">となっていることが確認できます。CSSで.titleセレクタにスタイルを定義すれば、見た目が変わります。
課題2: classを使ってみる
import './App.css'
function App() {
return <h1 class="title">Hello, React!</h1>
}
export default App
ブラウザのコンソールに以下のような警告が表示されます。
Invalid DOM property `class`. Did you mean `className`?
classはJavaScriptの予約語(クラス定義に使う)のため、JSXではclassNameを使う必要があります。React 16以降、誤った属性名も警告付きで文字列化されそのままDOMに渡されるためclass="title"自体は描画されますが、開発時に上記の警告がコンソールに表示されます。React公式の推奨は常にclassNameなので、新規コードではclassNameを使ってください。また、TypeScriptプロジェクトではclass属性は型エラー(Did you mean 'className'?)としてエディタ上でも検出されます。実行前に間違いに気づけるのはTypeScriptを使う利点の1つです。確認が終わったらclassNameに戻しておきましょう。
課題3: JavaScriptを埋め込む
import './App.css'
function App() {
const name = 'React'
return <h1>Hello, {name}!</h1>
}
export default App
波括弧{}内にJavaScriptの式を書くと、その評価結果が表示されます。変数だけでなく、{1 + 1}のような計算式や、{name.toUpperCase()}のようなメソッド呼び出しも可能です。
課題4: Fragmentを試す
import './App.css'
function App() {
return (
<>
<h1>タイトル</h1>
<p>本文です</p>
</>
)
}
export default App
<>と</>(Fragment)を使うと、余分な<div>を追加せずに複数の要素を返せます。HTMLの構造をシンプルに保ちたいときに使えます。開発者ツールで確認すると、<h1>と<p>が直接<div id="root">の中に配置されていることがわかります。
まとめ
- JSXはJavaScriptの中でHTMLライクな構文を書ける
- 単一のルート要素が必要(Fragmentで回避可能)
- 閉じタグは必須
classはclassName、属性はキャメルケース{}でJavaScript式を埋め込める
JSXに慣れないうちは、HTMLとの違いでエラーが出ることがあります。特にclassNameの使用、閉じタグの必須化、キャメルケースの属性名を意識しましょう。
次の章では、Reactの核となるコンポーネントについて学びます。