JSX(JavaScript XML)
React では、DOM API を使用して直接 DOM を操作するコードを書く代わりに、 DOM 要素と1対1で対応する React 要素 を JSX(JavaScript XML) というHTMLに似た記法で記述して、React 要素に対して操作を行います。
1. React 要素と仮想 DOMツリー
React 要素から構成されたDOMと同じようなツリーを 仮想DOMツリー や React DOMツリーと呼びます。仮想DOMツリーに新たな変更を加えると、以前の状態との比較が行われ、更新があった箇所のみ、実際のDOMツリーに反映する仕組みになっています。
仮想DOMは軽量なデータ構造になっており、仮想DOMの操作は実際のDOMを操作するよりも効率的です。実際のDOMの操作を必要最低限に抑えることで、高速なパフォーマンスを実現しています。
2. JSX で React 要素を記述する
JSX は JavaScript の構文拡張で、JavaScript ファイル内に HTML のタグと似た形式のマークアップ言語を直接記述できます。以下のオンライン変換ツールを利用すると、HTML から JSX へ簡単に変換できるので試してみましょう。
https://transform.tools/html-to-jsx
例として、次の HTML をこのツールを使用して変換します。
<h1 class="title">ペットにしたい動物ランキング</h1>
<ol id="ranking">
<li>犬</li>
<li>猫</li>
<li>カワウソ</li>
<li>ハムスター</li>
<li>うさぎ</li>
</ol>
「HTML to JSX」が選択されていることを確認して、中央のブロックへコピーしたコードを貼り付けてください。
変換された結果が右側のブロックに表示されます。
確認すると、次のように変換されています。パッと見た感じでは、HTMLと変わらないように思えますが、いくつか違いがあります。
<>
<h1 className="title">ペットにしたい動物ランキング</h1>
<ol id="ranking">
<li>犬</li>
<li>猫</li>
<li>カワウソ</li>
<li>ハムスター</li>
<li>うさぎ</li>
</ol>
</>
注目するポイントは次の2つです。
-
h1
タグのclass
属性がclassName
に変更されている
class
は JavaScript の予約語であるため使用できないことから代わりにclassName
を使う。 -
最上位の階層に名前がないタグ
<></>
が追加されている
JSX では必ず最上位のタグは1つだけとし、2つ以上を並列して書くことはできません。<></>
は<React.Fragment></React.Fragment>
という特別なタグの省略形です。<div>
のようなタグで囲むとそのまま実際のDOMにも反映されますが、<React.Fragment></React.Fragment>
はそれ自体はDOMには反映されず、内側の要素だけがツリーに展開されます。そのため、余分な要素を増やしたくない場合によく用いられています。
2点目は少し理解が難しいかもしれませんが、JSX は実際には Babel を使って次のような JavaScript のコードに変換されます。
// jsxの以下のタグは次のようにJavaScriptのコードに変換される
// <h1 class="greeting">Hello <i>Taro</i>. Welcome!</h1>
createElement( // Reactの要素オブジェクトが作成される
'h1', // 要素名
{ className: 'greeting' },
'Hello ', // これ以下は子ノード
createElement('i', null, "Taro"),
'. Welcome!'
);
これを見ると分かるように、結果として、createElement
関数で生成されるひとつのオブジェクトとしてまとめられます。最上位に複数の要素が並列して記述されていると、この変換ができないため、上記のようなルールになっています。
ここまでで見て来たように、JSX では HTML の各タグに対応するタグ(React要素)があらかじめ用意されています。記述方法も HTML と非常に似てはいますが、タグや属性の書き方など一部でルールが違うところがあるので注意してください。他にも、<br>
要素や <img>
要素のように終了タグがない要素については、<br />
や <img />
のように、>
の前に /
を入れて明示的に閉じる必要があったりします。こうした細かなルールの違いは、次章からの練習を通して少しずつ確認していきます。
3. JSX の記述練習
では実際に、JSXで React 要素を書いて簡単な Web ページを作成してみましょう。
開発ディレクトリの準備
「Reactの開発環境構築」で作成したディレクトリを次のように整理します。public と src の2つのディレクトリから、次のファイルだけを残して、後のファイルを削除してください。削除後のディレクトリの構造は次の通りです。
first-app
|--- node_modules
|--- public
| |--- index.html
|
|--- src
| |--- index.css
| |--- index.js
|
|--- .gitignore
|--- package.json
|--- package-lock.json
|--- README.md
メインページの編集
public ディレクトリにある 「index.html」 がこのアプリケーションのベースになるメインページです。ブラウザで読み込まれたこのページに対して React が動的にDOM 要素を追加します。具体的には、<div id="root"></div>
の内部が React の書き換え対象となるようにプログラムを作成していきます。index.htmlを開いて不要な記述を削除して、次の内容で保存してください。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root">
<!-- この要素の内部を React で動的に書き換える -->
</div>
</body>
</html>
ページ内に展開する文書を JSX で記述する
src ディレクトリにある 「index.js」 がこのページのメインのプログラムファイルです。これを次のように書き換えてみましょう。
import React from 'react';
import ReactDOM from 'react-dom/client';
// ▼ public/index.htmlに書かれている <div id="root"> をルート要素として指定
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<>
<h1 className="title">ペットにしたい動物ランキング</h1>
<ol id="ranking">
<li>犬</li>
<li>猫</li>
<li>カワウソ</li>
<li>ハムスター</li>
<li>うさぎ</li>
</ol>
</>
);
コードの先頭にある import
文では、React アプリケーションに必要な次のパッケージ内のモジュールからエクスポートされたオブジェクトを読み込んでいます。
react
:React のアプリケーション開発に必要なコア機能を提供するパッケージreact-dom/client
:クライアント側でアプリを初期化する際に使う機能を提供するパッケージ
最初に ReactDOM
オブジェクトの createRoot()
メソッドで、仮想DOMツリーのルートとなる要素を作成します。ここでは、先ほどの index.html の <div id="root"></div>
をルート要素とするために、その DOM 要素オブジェクトを引数として渡しています。このメソッドの戻り値は ReactDOM.Root
のインスタンスオブジェクトです。このオブジェクトの render()
メソッドへ JSX 形式の文書を渡して実行すると、ルート要素内にこの文書が展開されます。
React を起動してページを確認する
この開発ディレクトリがカレントになっていることを確認して、ターミナル上で以下のコマンドを実行してください。ローカルでサーバーが起動して、ページの動作を確認できます。
npm run start
デフォルトでは3000番のポートで待ち受けるようになっていますので、もしブラウザが自動的に立ち上がらない場合は、次のURLに手動でアクセスしてください。
正常に動作していると、以下のようにブラウザのビューポートにページの内容が表示されます。
このページを Google Chrome の Developper Tools で確認すると、<div id="root"></div>
内にroot.render()
メソッドに渡した JSX の文書が DOM 要素に変換されて、挿入されていることが分かります。
スタイルシートを読み込む
JSX 内では外部のスタイルシート(CSSファイル)を import
文で読み込むことができます。まずは読み込むスタイルシートを準備しましょう。 src ディレクトリにある index.css を、次の内容に書き換えて保存してください。
html{
background-color: whitesmoke;
}
.title::before {
content: "★";
}
.title::after {
content: "★";
}
ol#ranking {
list-style: decimal;
list-style-position: inside;
}
ol#ranking li {
font-size: 1rem;
}
ol#ranking li:nth-child(1) {
font-size: 2rem;
font-weight: bold;
color: goldenrod;
}
ol#ranking li:nth-child(2) {
font-size: 1.5rem;
font-weight: bold;
color: silver;
}
ol#ranking li:nth-child(3) {
font-size: 1.2rem;
font-weight: bold;
color: brown;
}
このスタイルシートを読み込む import
文を追加します。 ECMAScript Modules の import
文とは異なり from
キーワードはありません。 import "ファイルへのパスを表す文字列”;
と書くだけで読み込むことができます。
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css'; // ◀ 保存したスタイルシートのインポート
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<>
<h1 className="title">ペットにしたい動物ランキング</h1>
<ol id="ranking">
<li>犬</li>
<li>猫</li>
<li>カワウソ</li>
<li>ハムスター</li>
<li>うさぎ</li>
</ol>
</>
);
npm run start
で開発用サーバーを起動した状態でファイルを保存すると、自動的にページが更新されるようになっています。ブラウザの画面に戻り、スタイルシートが適用されていることを確認してください。
こちらも Google Chrome の Developper Tools で確認すると、<head>
要素内に <style>
要素として追加されていることが分かります。
また、スタイルを適用する方法は他にも、タグに直接 style
属性の値を設定するやり方があります。例えば、<ol>
要素へ適用するスタイルは次のように書くことができます。
<ol id="ranking" style={ // ◀ 外側の波括弧はJavaScriptのコードを示す括弧
{ listStyle: "decimal", // ◀ プロパティ名にハイフンは使用できないためキャメルケースにする
listStylePosition: "inside" } // ◀ 内側の波括弧はオブジェクトリテラルを表す括弧
} >
ここで注意したいのは、あくまでも JSX は JavaScript のコードであるということです。HTMLと同じように""
で囲んだ文字列で style
属性の値を指定することはできず、代わりにオブジェクトのプロパティとして、各CSSプロパティの値を指定する必要があります。
文書内に JavaScript のコードを記述する
ちょうど上の例でも出てきましたが、波括弧( {}
)を使うことで、JSXの文書内に JavaScript の 式 を記述することができます。ただし、 for
文や while
文のように評価結果が値にならない文は記述できない ため注意が必要です。
ここでは例として、ページのタイトルが文字列で、ランキングのリストが配列として次のように与えられた場合に、これを JSX のタグに変換する方法を見て行きましょう。
const pageTitle = "ペットにしたい動物ランキング";
const list = ["犬", "猫", "カワウソ", "ハムスター", "うさぎ"];
まず、{pageTitle}
のように変数名を直接書けばそれが参照する値がそのまま埋め込まれます。
<h1 className="title">{pageTitle}</h1>
また、配列やマップのようなイテラブルなデータの場合は、要素が自動的に展開されて値が埋め込まれます。そのため、配列の場合は map()
メソッドや filter()
メソッドと組み合わせることで、簡潔に記述することができます。
<ol id="ranking">
{ // map メソッドの戻り値のli要素の配列がそのまま展開される
list.map((val, index) => {
return <li key={index}>{val}</li> // 上の例では省略していたが、本来はul要素内で各li要素を識別するためにkeyプロパティに一意な値を設定する必要がある
})
}
</ol>
以上のことを踏まえた上で、index.js 全体を書き直すと次のようになります。
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
const pageTitle = "ペットにしたい動物ランキング";
const list = ["犬", "猫", "カワウソ", "ハムスター", "うさぎ"];
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<>
<h1 className="title">{pageTitle}</h1>
<ol id="ranking">
{
list.map((val, index) => {
return <li key={index}>{val}</li>
})
}
</ol>
</>
);
ここまでで JSX について簡単に紹介してきましたが、より詳いことは以下の React 公式ドキュメントにおいて、サンプル付きで丁寧に解説がなされているので、参考にして各自で学習してください。
次のセクションでは、 React 重要なコンセプトのひとつである「コンポーネント」について説明します。