【React】ReactElementとJSXとComponentを大雑把に理解する

対象 React初心者

書いている人 プログラミング歴2年目

前提 Reactを少し触ったことがある

環境 M1Mac React ver18

参考 Reactドキュメントhttps://ja.reactjs.org/docs/hello-world.html

Reactを使用していて、「{ }ってどこで使えるんだっけ?」「あれ、ここにこれ書いちゃいけないんだっけ?」「何でこれエラーになるの?」「結局JSXって何?」と細かく理解しないまま使っていた為につまずくことが多かった。

そこで、改めて基礎を理解しようと思って記事をまとめたい。

ReactドキュメントのMainConseptの部分を参考にまとめています。

Reactは、ドキュメントが日本語でも比較的読みやすいと思うので、まだ読んだことない人はぜひ読んでみてください。

※この記事では正確に意味を汲み取りたいため、英語版を参照しています。

目次

ReactElementとは

Documentを読んでいたらReactを理解するためには、タイトルにも書いた「ReactElement」を理解することが一番なのではないかと思った。「React要素」と日本語では訳されているけど、ここでは原文のまま使っていきたい。

ReactElementとは、ドキュメントから引用すると、画面に表示したいものの説明書きのことだ。
もっと言うとJavaScriptのobjectみたいなものだ。

構造的にシンプルにしたものらしいので正確ではないけれど、概ねobjectだと思って良いと思う。

ReactではこのReactElementを、React.createElementメソッドを使って作成している。

公式のcreateElementメソッドより

createElementメソッドの引数にはtypeが入る。このtypeには、<div> や<span>などのtagの名前やReact componentを受け取り、後はオプションとして、要素の属性や子要素を入れられるみたいだ。

以下のような感じでcreateElementメソッドの引数に必要なものを入れられる。

1行目がtype、2行目が属性、3行目が子要素だ。

実質的には、以下のようなオブジェクトに変更されるというわけだ。

結局はobjectに変換される

Reactを触ったことがある勘のいい方ならもうわかったかもしれないが、引数にタグを渡したり、属性を渡したり、子要素を渡したり、これ何かに似ていないだろうか?

そうJSXだ。

Reactを触っていたらお馴染みの形、JSX。

実はJSXは、ReactElementを作成しているのだ。

JSXはReact Elementsを作成している。

正確に言えば、BabelというコンパイラがcreateElementメソッドを裏で呼んでいるようだ。

Babelすごい

JSXを使えば、裏でBabelが動いてくれているので明示的にcreateElementメソッドを呼ぶ必要はない。

赤線部分で明示的に呼ぶ必要がないことが書かれている。

ここでは、JSXがReactElementを生成していることを押さえておこう。

さて、JSXがReactElementになることは分かったけれど、Webページに表示するためにはDOMに変更する必要がある。ReactElementはどうやってDOMに変換されるのだろうか?

次にその部分を見ていく。

renderメソッドにReact要素を渡す

ReactElementがどうやって変換されるか見る前に、まずは、Reactプロジェクトを作成しよう。

npx create-react-app my-appコマンドでReactアプリを作成しよう。(nodeが入っている必要があります。)

プロジェクトを作成したら、index.jsファイルを開こう。ちなみにReactのバージョンは18だ。

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

7行目から12行目まがズバリReactElementをDOMに変更しているところだ。

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

7行目のReactDOM.createRoot(document.getElementById('root'));の部分でまず、ReactDOMから、createRootメソッドを呼び出している。

このメソッドの中で、document.getElementById('root')を呼んでいるが、これはJavaScriptでよく見るやつHTMLのrootという名前のid属性を取得するメソッドだ。

このrootと書かれたid属性は、public/index.htmlファイルの中に記載されている。

3行目で、<div id="root"></div>を取得している。

<body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

      You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the <body> tag.

      To begin the development, run `npm start` or `yarn start`.
      To create a production bundle, use `npm run build` or `yarn build`.
    -->
  </body>

ReactはSPA(シングルページアプリケーション)といって、1つのHTMLファイルだけを使用し、DOMを変更していくことでページを作成していくJavaScriptのライブラリだ。このindex.htmlで取得したdiv要素の中に、Reactの記述されたコンポーネントが表示される仕組みだ。

createRootメソッドで選択したidを取得したら、次にrenderメソッドを呼ぶ。そのメソッドの中に、ReactElementを入れることで、取得したid内でDOMが生成されるという仕組みだ。

root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

<App />はコンポーネントだが、後述する通り、コンポーネントもReactElementだ。
後は、<App />コンポーネントの中で、他のコンポーネントを呼んでいくことでいつものReactアプリを作成することができる。

ここでは、本当に表示されるのか、試しにrenderメソッドの中身を以下のように書き換えてみる。

const root = ReactDOM.createRoot(document.getElementById('root'));
const element = <h1>Hello, world</h1>;
root.render(
  element
);

すると、ブラウザ上で表示が書き変わるのが確認できるはずだ。

表示が変更された。

renderメソッドを使うと、ReactDOMがReactElementをDOMに変換してくれる


もし、中身に変更があった場合は以前のReactElementと変更されたReactElementを比較して、違いがあるなら差分だけ表示する仕組みになっているので、ページリロードが発生せずに画面が表示され、ユーザーにストレスをかけさせない仕組みになっている。


React ver18ではcreateRootが使われているが、それ以前は、別のモジュールのrenderメソッドを使っていた。18とそれ以前では、importしてるモジュール名が違うのがわかる通り、使われるメソッドが違っているのに注意だ。

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

上は、ver18以前のindex.js。renderメソッドにReactElementとgetElementByIdしてきたものをまとめて引数に取っている。

レガシーメソッドになっているのが分かる。

ここまでいったんまとめると、

  • ReactElementは画面に表示したいものの説明書き。中身は実質JavaScriptのobject。
  • JSXはReactElementを作る。裏でBabelがcreateElementメソッドを読んでReactElementに変換している。
  • ReactElementを作成して、createRootから呼ばれるrenderメソッドに入れると、レンダリングされる。

JSXとは

JSXとはJavaScriptの構文を拡張したもので、簡単に言うと、JavaScriptでHTMLのタグみたいなものが書ける。

公式より。「JavaScriptの構文の拡張です」と書かれている。
変数を宣言して、その中でHTMLのタグを使っている。

ReactElementの欄でも説明したが、JSXはBabelが裏でcreateElementメソッドを読んで、ReactElementに変換される。

JSXを使わないでReactを書くこともできるが、使わない理由はないと思う。

JSXの中で{}を使うことで、その中でJavaScriptの変数や関数を展開することができる。

nameという変数をJSXの中で展開している。
JSXの中で関数を展開している。

また、JSX自体も関数の中に使うことができる。

関数の中でJSXを返してる。

さらに詳しい使い方は公式を見て欲しい。

JSXとは:https://reactjs.org/docs/introducing-jsx.html

注意しなくてはならないのは、for文やif文はJSXの中で使うことはできないと言うこと。
これは自分もよくやってしまっていたので、if文やfor文を使うときは関数で定義してから、それをJSXの中で使用すればよいかも。

もっと詳しく説明すると、{}の中ではJavaScriptの式は使えるけど、文は使えないらしい。
この式と文の違いは以下を参考にしてみてください。

式と文の違い : https://jsprimer.net/basic/statement-expression/

Componentとは

ComponentはReactの中で一番重要な概念でこのComponentを組み合わせて画面を構成していく。

Componentには、クラスコンポーネントと関数コンポーネントの2種類があって、現在、専ら主流なのは関数コンポーネントだ。ここでは関数コンポーネントについて見ていく。

上が関数コンポーネントで下がクラスコンポーネントだ。

現在のReactドキュメントは主にクラスコンポーネントを中心に書かれているので、関数コンポーネントを中心に書かれた新しいドキュメントが現在作成されている。最終的にはドキュメントがこちらに置き換わるようだ。
Beta版:https://beta.reactjs.org/


関数コンポーネントは、JavaScriptの関数定義と書き方が一緒で、引数にpropsというオブジェクト(propertiesの略)を受け取ることができる。

returnで返しているのは見てわかる通り、JSXだ。

このコンポーネントはまた頭を大文字にしてタグのように使い、変数の中に入れることができる。

変数にコンポーネントを入れる。必ず頭は大文字で書く必要がある。

コンポーネントがレンダリングされる時は、以下のような処理が行われる。

createElementが呼ばれているみたいだ。

見たらわかる通り、コンポーネントもレンダリングする際に、createElementメソッドが呼ばれている。

createElementメソッドは、上にも書いた通り第一引数にtypeを受け取る。それは、<div />などのタグか、Reactコンポーネントか、フラグメント(空のタグ<></>を受け取る。だから、JSXではタグが2つになるような書き方はできない。

createElementでコンポーネントが呼ばれた場合に、引数としてそこに書かれた属性がpropsとして、そのコンポーネントに渡される。propsはオブジェクトの形で渡されるので、受け取るコンポーネント側では分割代入などでも受け取ることができる。

<Welcom name="Sara" />というコンポーネントの属性name="Sara"{ name: Sara }となって、Welcomeコンポーネントのpropsに渡されるというわけだ。ここでは、props.nameとして値を取り出して、returnの中に埋め込んでいる。

引数propsの部分に{ name: Sara }が入ってくる。

コンポーネントの中でJSXが使われているが、結局コンポーネントも<Welcom name="Sara" />のようにタグとして、使えるので、コンポーネントもJSXと言っていいと思う。(ドキュメントではおそらく明記されていなかったが、そういうニュアンスで使われている)

まとめ

  • ReactElementは画面に表示したいものの説明書き。中身は実質JavaScriptのobject。
  • JSXはReactElementを作る。裏でBabelがcreateElementメソッドを読んでReactElementに変換している。
  • ReactElementを作成して、createRootから呼ばれるrenderメソッドに入れると、レンダリングされる。
  • Componentには関数コンポーネントとクラスコンポーネントの2つがあり現在の主流は関数コンポーネント。
  • Componentはpropsという引数を受け取る。また、<Welcome />とタグとしても使える。
  • コンポーネントも裏では、createElementメソッドが呼ばれる。その際に、属性がオブジェクトとしてpropsに渡される。

色々書いたが、大きなポイントとしては2点だと思う。

createElementメソッドが裏で呼ばれることにより、ReactElementになる。
作成したReactElementをrenderメソッドに渡すことで表示される。

この2点を押さえておけば大まかな概念は理解できると思う。

結局ドキュメントを見るのが遠そうに見えても、一番その言語やフレームワークを習得する近道になると思った。英語ができる人ならばまず間違いなく英語でドキュメントを参考にするのがよいと思う。
概念を押さえることで前よりReactが馴染みあるものになっていただければ幸いです。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次