このセクションもいよいよ佳境です。今回はTSXの正体について学習していきます。
これまで何度か言及してきましたが、TSXはあくまでTypeScriptのオブジェクトとして扱うことができます。一見ただのHTMLに見えますが、オブジェクトなので変数に代入したり、propsとして他のコンポーネントに渡したりできるわけです。
コンソールで中身を見てみる
まずは実際に確認してみましょう。
//Greet.tsx
import "./Greet.css"
const Greet = ()=> {
const sample = <h3>Hello TSX</h3>;
console.log(sample);
return (
<div className="component">
{sample}
</div>
);
}
export default Greet;
HTMLではなく、TypeScriptのオブジェクトが表示されました。
TSXは関数呼び出しに変換される
ではどうやってオブジェクトになるのでしょうか?
TSXは、ビルド時に関数呼び出しの形に変換されています。<h3>Hello TSX</h3>は、現行のReactではこう変換されます。
import { jsx as _jsx } from "react/jsx-runtime";
_jsx("h3", { children: "Hello TSX" });
この_jsx()が、オブジェクトを作るための関数ということになります。
なお古い解説では、変換結果がReact.createElement("h3", null, "Hello TSX")と書かれていることがあります。こちらはReact16以前の変換方式で、React17からreact/jsx-runtimeを使う方式が導入されました。
第三回で作成したNext.jsのプロジェクトは新しい方式の設定になっているので、import React from "react"を書かなくてもタグが動きます。
できあがるオブジェクトが「React要素」
この関数が実行されてできるオブジェクトをReact要素と呼びます。
{
type: "h3", // タグの種類
key: null, // リスト表示で使う目印
props: { children: "Hello TSX" }, // 属性と、タグに挟んだ中身
}
第1引数のタグの種類がtypeに入り、属性がpropsに入ります。
そしてここが重要なのですが、タグに挟んだ中身はprops.childrenに入ります。前回childrenが特別に見えたのは、Reactが特別なルールを作ったからではなく、変換の時点でそこに詰め込まれているからです。
オブジェクトとして操作できる
オブジェクトである以上、オブジェクトにできる操作は何でも行えます。
//Greet.tsx
const Greet = ()=> {
console.log((<div className="component">Hello</div>).props);
return (
<div className="component">
<h3>Hello World</h3>
</div>
);
}
コンソールには{ className: "component", children: "Hello" }と表示されます。propsオブジェクトを取り出すこともできました。
入れ子にするとツリーになる
タグが入れ子になっている場合はどうなるでしょうか。第五回で作成したGreetはこういう構造でした。
//Greet.tsx
const sample = (
<div className="component">
<h3>Hello World</h3>
<List />
</div>
);
この場合、divのReact要素のchildrenの中に、h3とListのReact要素が入ります。
div のReact要素
├── h3 のReact要素
└── List のReact要素
コンソールでchildrenを開いていくと、実際にそうなっていることが確認できます。
このように入れ子になった場合、React要素はツリー状に構成されていきます。こういったものをReact要素のツリーと呼びます。
Reactはこのツリーをもとにして画面を構成するので、このコンセプトはぜひ覚えておいてください。
用語の整理
| 用語 | 意味 |
|---|---|
| JSX | HTMLに似た記法そのものの正式名称。ツールの設定名にもこの名前が使われる |
| TSX | JSXをTypeScriptファイル(.tsx)で書いたもの。中身はJSXと同じ |
| React要素 | 変換後の関数が実行されてできるオブジェクト。type / props / keyを持つ |
| React要素のツリー | React要素が入れ子でつながった構造 |
| 仮想DOM | 上のツリーを指す通称 |
ここで1つ疑問
ここまで読み進めてきた中で、ある疑問を抱いた方がいるかもしれません。
それは、**コンポーネント自体はReact要素なのか?**ということです。
React要素の定義は「変換後の関数が実行されてできるオブジェクト」でした。それを前提に、少し考えてみてください。
確認問題
以下のTSXについて考えてみましょう。
<h3 className="title">Hello World</h3>
問題
- このTSXが変換されたあとの関数呼び出しを書いてください
- その関数が実行されてできるReact要素の
typeとpropsが、それぞれどうなるか答えてください -
Hello Worldという文字列は、React要素のどこに入るか答えてください
(下に解答を記載しています)
↓
↓
↓
↓
↓
↓
↓
↓
↓
↓
解答
1. 変換後の関数呼び出し
_jsx("h3", { className: "title", children: "Hello World" });
2. React要素の中身
{
type: "h3",
key: null,
props: { className: "title", children: "Hello World" },
}
typeにはタグの種類である"h3"という文字列が入り、propsには属性が入ります。
3. 文字列が入る場所
props.childrenに入ります。
タグに挟んだ中身はchildrenというプロパティに詰め込まれる、というのが変換のルールです。前回学んだchildrenが特別に見えたのは、この仕組みによるものでした。
チェックリスト
- TSXはビルド時に関数呼び出しに変換されると理解している
- 変換後の関数が実行されてできるオブジェクトをReact要素と呼ぶ
-
React要素は
type/props/keyを持っている -
タグの種類が
typeに、属性がpropsに入る -
タグに挟んだ中身は
props.childrenに入る -
console.logでReact要素の中身を確認した -
React要素からは
.propsのようにオブジェクトとして値を取り出せる - 入れ子にするとReact要素のツリーになると理解している
- 仮想DOMがこのツリーを指す通称だと知っている
第十三回はここまで。
次回は実際にコードを動かしながら、この疑問について確認していきます!