はじめに
前回の記事は以下から
React未経験なので公式チュートリアルから学んでみる Part1
引き続き、私が学んだことをまとめていきます。
前回はReactについての大まかな部分について触れました。
今回はJSXについてもう少し深く掘り下げて確認します。
REACTを学ぶ-JSX でマークアップを記述する
の内容がベースになっています。
JSXとは
JSXの概要
まず初めに理解しておきたいのは、ReactとJSXは別物であるということです。
JSXはJavaScriptを拡張したもので、XMLのような構文でマークアップを記述できます。
一方、ReactはUIを表示するためのJavaScriptのライブラリであり、両者は異なるものです。
次にJSXはJavaScriptオブジェクトであるということです。
見かけ上はHTMLですが、その中身はオブジェクトです。
これら2点について順にみていきます。
JSXを理解する
冒頭でJSXはJavaScriptの拡張であり、Reactとは異なると言いましたが、拡張であるとはどういうことでしょうか。
JSXの中身から考えていきましょう。
JSXを使わない書き方
ReactとJSXは別物であるので、JSXを使わずにReactを記載することが可能です。
試しにやってみます。App.jsを編集していきましょう。
(App.jsについては初回記事のこちらを参照してください)
reactからcreateElement
をimportし、App関数の中身を書き換えます。
createElement
関数の第1引数にhtmlのタグを、第2は使用しないのでnull、第3引数にタグに設定する中身を設定します。
import logo from './logo.svg';
import './App.css';
import { createElement } from 'react';
function App() {
return createElement('h1', null, 'Helllo World!');
}
export default App;
JSXを使用していませんが、「Hello World!」が出力されます。
Reactで用意しているcreateElement
関数が引数から画面表示に必要なオブジェクトを生成し、返却しているため、JSXなしでも画面描画ができるのです。
ただ、今回は<h1>
タグのみでしたが、htmlの要素が増えてくると、この関数で表現するのは少し大変そうです。
JSXの方が簡潔に書けますし、なによりhtmlの記載とほぼ変わらないので直感的にもわかりやすいかと思います。
JSXで先ほどと同じ挙動を実現するには以下のようになります。
function App() {
return (
<h1>Hello World!</h1>
);
}
export default App;
では、一見するとただのhtmlで、関数などの処理は見えないのに、なぜJSXの記載方法で結果が正しく表示されるのでしょうか。
その中身について次に確認します。
JSXの変換
先ほどの問いの答えは単純で、JSXの記載は裏でJavaScriptの構文に変換されているためです。
その変換に使われているのがBabelと呼ばれるものです。
Babelの変換については以下のサイトをみてください。
Babel-Try it out
このサイトではBabelを使った変換結果がどうなるかを確認できます。
今回はこちらのJSXを変換してみましょう。
<h1>Hello World!</h1>
変換の際、左下の「OPTIONS」の「React Runtime」が「Classic」になっていることを確認してください。
JSXを左側に入力すると、変換結果が右側に表示されます。
React.createElement
が使われていることがわかります。
先ほどJSXを使わずに書いたときに使った関数とまったく同じです。
つまり、内部ではJSXにおいても、Reactが用意しているcreateElement
のような関数を呼ぶ形に変換されているということです。
このようにJSXはあくまでも便利に書くための書き方であり、これがJavaScriptを拡張したもの、ということになります。
※Reactの17からは上記の変換は使われていないようです。
参考:Zen - React17におけるJSXの新しい変換を理解する
Babelのサイトで新しい変換方法で試す場合、「React Runtime」を「Automatic」にすると、どのような形か確認することができます。
オブジェクトとしてのJSX
次に、JSXがオブジェクトであることを確認してみましょう。
先ほどと同じようにApp.jsを編集します。
JSXを変数に格納し、その変数をreturnしています。
また、変数の型と、変数自身をコンソール出力するようにしています。
import logo from './logo.svg';
import './App.css';
function App() {
const obj = (<h1>Hello World!</h1>);
console.log(typeof(obj));
console.log(obj);
return obj;
}
export default App;
画面には「Hello World!」が出力されると思いますが、画面ではなく開発者モードからコンソールを確認してみましょう。
typeof
の出力結果は「object」と出力され、objの中身が出力されています。
key
やprops
、ref
、type
などのキーを持っていることがわかります。
このことから、JSXがオブジェクトであることが確認できます。
ここで注目すべきはprops
とtype
です。
type
には「h1」
props
には「children: 'Hello World!'」が設定されています。
つまりJSXとして渡している
<h1>Hello World!</h1>
が、内部の処理で上記のようなオブジェクトに変換されている、ということがわかります。
なお、何の説明もなく
const obj = (<h1>Hello World!</h1>);
と記載していましたが、JSXはオブジェクトであるので問題なく変数に格納できます。
この変数obj
を返却することで、return文にJSXを書くのと同じ挙動になります。
JSXを使う
JSXがどのようなものか、は理解できたかと思います。
では、実際にJSXを使ってマークアップを記述できるようになりましょう。
そのためには3つのルールを押さえておく必要があります。
1. ルートタグは1つ
以下のようなhtmlのタグがあったとします。(bodyタグ内のみ抜粋)
通常のhtmlであれば、問題なく画面表示ができます。
※正しく動くことは以下のサイトから確認できます。
liveweave
<h1>title1</h1>
<h2>title3</h2>
<h3>title3</h3>
これをJSXで記載してみましょう。
App.jsに転記します。
import logo from './logo.svg';
import './App.css';
function App() {
return (
<h1>title1</h1>
<h2>title3</h2>
<h3>title3</h3>
);
}
export default App;
結果はエラーになるはずです。
エラーメッセージから分かる通り、隣り合うタグはタグで囲まれている必要があります。
つまり、トップとなるルートのタグは1つしか許容されません。
先ほどのApp.jsを修正するのならば、
function App() {
return (
<div>
<h1>title1</h1>
<h2>title3</h2>
<h3>title3</h3>
</div>
);
}
となります。
ルートタグを<div>
タグの1つのみとし、小要素として他のタグを設定しています。
しかし、これだと余計な<div>
タグが増えてしまうのがやっかいです。
※<div id="root">
はApp.jsではない別のファイルで設定されているものです。
そこで、以下のように書くこともできます。
function App() {
return (
<>
<h1>title1</h1>
<h2>title3</h2>
<h3>title3</h3>
</>
);
}
開発者モードから確認すると、空のタグ<>...</>
は出力されていないことが確認できます。
これはフラグメントと呼ばれるものです。
<fragment>...</fragment>
と書くこともできますが、一般的には省略して空のタグで書くようです。
フラグメントを使えば、htmlの中身に影響を与えることなく、複数の要素をまとめることができます。
2. 必ずタグは閉じる
htmlのタグの中には、終了タグを省略できるケースがあります。
<ul>
<li>apple
<li>banana
<li>cherry
</ul>
<input type='text'>
<li>
タグは終了タグの</li>
を省略できます。
また<input>
タグはそもそも終了タグが存在しません。
これらをJSXに記載してみましょう。
function App() {
return (
<ul>
<li>apple
<li>banana
<li>cherry
</ul>
);
}
function App() {
return (
<input type='text'>
);
}
結果はいずれもエラーになります。
<li>
タグでのエラー
<input>
タグでのエラー
JSXの方がhtmlよりも厳密であるので、必ずタグは明示的に閉じるようにしましょう。
function App() {
return (
<>
<ul>
<li>apple</li>
<li>banana</li>
<li>cherry</li>
</ul>
<input type='text' />
</>
);
}
<input>
タグのような、そもそも終了タグがないものについては、末尾にスラッシュを入れることで閉じたことになります。
※htmlでも、スラッシュを末尾に入れてかまいません。正しく表示できます。
3. 属性はキャメルケースで
htmlのタグにはidやclassなどの一般的な属性、またonclickなどのイベントハンドラー属性があります。
<input id="input_form" class="required">
<button onclick="(()=>alert('clicked'))();">click me!</button>
いったん上記のままJSXとして書いてみます。
function App() {
return (
<>
<input id="input_form" class="required" />
<button onclick="(()=>alert('clicked'))();">click me!</button>
</>
);
}
一見、何のエラーもないように見えます。
開発者モードのコンソールを確認してみましょう
class、onclickともにエラーとなっていることがわかります。
まず、classについてはJavaScriptではclassが予約語になっており、classを使うことができません。
JSXはJavaScriptオブジェクトであり、オブジェクトに変換される過程で、属性がキーとして設定されます。
予約語はキーには使うことができないため、エラーになっています。
class属性を設定したい場合は、代わりにclassNameを使います。
また、onclick属性もエラーになっています。
これは、reactでの属性の多くがキャメルケースで用意されているためです。
よって、先ほど確認したclassNameも含め、属性についてはすべてキャメルケースで記載するようにしましょう。
正しく記載すると以下のようになります。
function App() {
return (
<>
<input id="input_form" className="required" />
<button onClick={() => alert('clicked')}>click me!</button>
</>
);
}
※onClickの中身が先ほどの例と少し変わっています。こちらの記法については次回以降で説明します。
まとめ
JSXの中身の詳細、およびJSXでマークアップを記載するためのルールを確認しました。
これで、簡単なWebサイトの見た目くらいであれば作ることができるでしょう。
ただ、これだけではまだまだReactの機能を理解しきれていません。
次回はJSXの中でJavaScriptの変数を利用する方法について確認します。
Part3はこちら
(次回の記事は鋭意作成中です。)