はじめに
Reactを習得してから2年くらい経ちますが、未だに初めてReactを見た時の衝撃は思い出せます。
何故なら今まで知っているJavaScriptとは全く違うものだったからです。
また、Reactは独自の概念が多いため、初期の学習コストが高いです。JSXやら仮想DOM、宣言的UIといった概念から、モダンJS開発の初心者はnode.jsやwebpackといったツールの使い方も覚える必要があります。
なので、今回はJavaScriptを学習したてくらいの人に向けた、Reactをなんとなく理解するための記事を書いてみました。
Reactに興味があるけど学習に至ってないよ~という人は是非ともここでなんとなく理解していってください。
尚、今回の記事はなんとなく理解させるために正確性を欠いている部分があるので、その点は実際に学ぶときに補完していただく、という事でご了承ください。
1.Reactは何をするライブラリ?
簡単に言うと「データに応じて自動でHTMLを更新する」 ライブラリです。
Reactは主に「変数の変更を検知して自動でElementを作成・更新・削除する」機能を提供します。
これにより、複雑な動きをするUIを簡単に作成できます。
例
どれくらい簡単になるかをざっくりと理解するために、具体的なコードを見てみましょう。
このようなカウンターアプリを作る事を考えます。
左にHTML+JS、右にReactのソースコードを提示します。
ぱっと見でも記述量が減っていることが分かると思います。
Reactのいいところ
1.データの変更を自動で反映する
従来、データの変更を反映するにはJavaScriptでElementを呼び出して、
変数をElement内に代入する処理を作成する必要がありました。
Reactでは「この要素内ではこのデータを使うよ!」と一度宣言してしまえば、後はReactが勝手に変数の変更を検知して画面を書き換えてくれます。
これを一般的にはデータバインディングと言います。何となく頭に入れておくとよいでしょう。
ただし、それをするにはReactのお作法(構文)に沿う必要があります、
それがJavaScriptにHTMLを合体した構文「JSX」 です。
JSX構文について
このJSX構文を使うと、データバインディングした変数の状態に応じて、
従来JavaScriptで直接記述していた「getElementBy~」や「createElement」を
Reactが裏で自動実行してくれるようになります。
つまり実装時にDOM要素の変更処理を書かなくて良くなるわけです。
例えば、HTMLに変数や関数を紐づけるには、HTMLテンプレート部(JSX)に{}
を付けます。
このように、HTML要素とJavaScriptの関連性が分かりやすくなるのもメリットの1つです。
また、Reactは値を変更すると自動的に画面に反映できる特殊な変数(状態変数)を用意できます。
これはReactの関数useState
を使う事で可能です。
useStateは状態変数とセッター関数を返す関数です。
[]での代入は見慣れないかもしれませんがJSの標準的な構文です。
2.UIを部品毎に管理できる
アプリ開発の際は、「ボタン」や「テキストボックス」「ヘッダー」など、
複数画面で使う共通部品や、動的に追加できるようにしたい部品、リスト表示する部品など、
通常のHTMLとは分割して管理したいようなUI部品が存在します。
ただ、HTML+JSで分割管理するのは割と面倒です。
ですが、Reactを使えばUI部品を簡単に分割できます。
例えば、先程のカウンターアプリ「CounterApp」を別のアプリの一部に組み込みたいとします。
その場合はこうするだけで良いです。
Reactは「HTMLを返す関数 = 部品」と解釈するため、
HTMLテンプレート(JSX)内に<関数名/>と書くだけで簡単にUI部品を呼び出すことができます。
別ファイルの部品を使う場合はimport文で呼び出すだけです。
これにより、大規模アプリを効率よく作成できたり、動的に要素を追加しやすくなるといったメリットがあります。
要するに
このように、ReactはHTMLをJavaScriptで動的に操作するのに便利な機能を多数持っているため、昨今のwebアプリを作る際にはよくReactが使われています。(NetfilxやUber Eats等)
あれ?これって使えるの?
ここまでの話を聞いて、初心者の方はこれを見て思ったのではないでしょうか。
「Reactって結局どうやってブラウザで読み込むんだ?」と。
その疑問はごもっともです。何故ならブラウザが最終的に画面に表示するのはHTML構文で書いたものだけであって、Reactの構文(JSX)は現時点でブラウザは解釈できません。
そのため、Reactで書かれたファイルはコンパイラでJavaScriptに変換してからHTMLで読み込む必要があります。
3.Reactはどうやって使うのか?
1.コンパイル
JSXはそのままではブラウザで読み込めないため、
コンパイラを使ってJSXをJavaScriptに変換する必要があります。
ローカルPCでコンパイルするのが一般的です。(一応、scriptタグでコンパイラを呼び出して、その場でJSXをコンパイルする方法もあります)
コンパイラとしては「babel」「webpack」「vite」等がよく使われます。
これらは全てjavascriptのパッケージマネージャー「npm」でDLできます。
(npmが分からない場合は後で解説するのでお待ちください)
コンパイル前(.jsx)
function App() {
return <h1>Hello World</h1>;
}
export default App;
コンパイル後(.js)
import React, { Component } from "react";
function App() {
return React.createElement("h1", null, "Hello, World!");
}
export default App;
このように、JSXはReact.createElementという関数に変換されます。言ってしまえばこれがReactの正体であり、JSXはあくまでもReactの関数の代わりとして開発時に使える構文に過ぎません。
ちなみに、「コンパイル」は厳密には「低レベル言語への変換」を指す言葉なので、今回のようにJSXをJSに変換するような「高レベル言語から高レベル言語」への変換処理は「トランスパイル」と呼ばれます。
今回は面倒なのでコンパイルと言いますが、頭の片隅に入れておきましょう。
2.バンドル
さて、コンパイルが完了しましたがこのままだとHTMLで呼び出すには不向きです。
特に大規模アプリだと、部品毎に大量にファイルを用意することになるので、HTMLのscriptタグも大量に用意してファイルを読み込まないといけません。
そのため、基本的にはバンドラーツールを使って1つのJSファイルにまとめられます。
バンドラーツールは「webpack」「vite」等がメジャーです。
これは良く使われるwebpackのイメージ図ですが、このようにバンドラーツールは多数のjsファイルやcssファイルの依存関係を1つまたはいくつかのファイルに纏めて指定フォルダに出力する事ができます。
これにより、モジュール別にコードを管理しつつ、HTMLで呼び出しやすい形にすることが出来るようになります。
3.HTMLで呼び出し
1つ説明していなかったことがありますが、Reactを使うにはHTML上のどこに描画するかを指定する必要があります。
一般的には、HTMLに「root」というidを持ったdiv要素を配置して、そこに対してReactのエントリポイントを設定する事が多いです。
これをしないとHTMLとReactは連携できないので注意してください。
さて、コンパイル+バンドルしたことにより、HTMLで呼び出す準備が整いました。
バンドラーツールによって、「dist」ディレクトリ内に「main.js」が最終的に作成されるとします。
その場合、HTMLではこのように呼び出します。
これにより、コンパイルされたReactのコードが読み込まれ、<div id="root"></div>
の箇所にReactが動的にUIを描画するようになります。
4.Reactの開発環境
従来のバニラJSやJQueryを用いたwebアプリの開発環境は、ブラウザとテキストエディタがあれば十分でした。何故なら生のJSなので何もしなくてもブラウザが解釈できるからです。
ですがReactはJSXなのでそうはいきません。
しかもJSXのコンパイラはパッケージマネージャーから使う事を想定されていますので、
JavaScriptのパッケージマネージャー「npm」をインストールする必要があります。
そのため、まず必要になるのがnode.jsです。
1.Node.JSのインストール
Node.JSは、従来ブラウザ上でしか動かせなかったJavaScriptをPC上で動かせる実行環境です。(実態はNode.exeファイル)
これにより、JavaScriptでOSの機能(ファイルアクセスやネットワーク通信)が行えるようになります。
また、Node.JSをインストールすると、JavaScriptのライブラリを簡単に管理できる「npm」 というパッケージマネージャーもついて来ます。
Reactアプリを開発する際は、この「npm」を使う必要があります。
ちなみにNodeJSについてもっと知りたい際にはこちらの記事が分かりやすいので紹介しておきます。
2.Reactのインストール
npmが使えるようになったら、コマンドを使ってReactの開発環境が構築できるようになります。
今回は個人的におススメの「Vite」を使った環境構築方法を説明します。
Viteはnpm上で実行できるのでインストールする必要はありません。
npm create vite@latest
このコマンドを実行すると、まずプロジェクト名を聞かれます。入力した名前のフォルダがコマンドを実行した際のフォルダの下に出来ます。
次に、使用するJSフレームワークを聞かれるので「react」を上下キーで選択します。
次にTypeScriptを使うか聞かれます。TypeScriptはJavaScriptに「型」を明示的に付けられるJavaScriptの拡張言語です。最近はTypeScriptが使われることが多いと思います。ひとまず今回はJavaScriptを選択してみます。
すると先程入力したプロジェクト名のフォルダが生成され、その中にテンプレートファイルが置かれます。
しかし、この状態は必要なライブラリがインストールされていない状態になっているので開発はできません。
なので以下のコマンドを実行します。
cd プロジェクトフォルダ名
npm install
npm install コマンドは、package.json内に書かれた「使用するライブラリ(パッケージ)」の情報を元に、必要なライブラリを全てインストールしてくれるコマンドです、※proxy環境だと設定が必要なので注意してください。
インストールが完了するとnode_modulesフォルダ内にインストールされたライブラリが格納されます。実際にwebサーバーに配置する際はこのフォルダ内にあるライブラリも含めて1つのJSファイルにバンドルする事になります。
開発を行う際には、npm run dev
コマンドを実行します。
このコマンドはpackage.jsonの「scripts」で定義されたスクリプトコマンドで、必要であればこのscripts内にユーザー定義のショートカットコマンドを作成する事が出来ます。
コマンドを実行すると、開発用サーバーがローカルに立ち上がります。
コマンドラインにサーバーのURLが出力されるのでそこにアクセスするとこのような画面が表示されます。
この開発サーバーは「ソースコードを変更すると、即座にコンパイルして画面に反映する」 という非常に便利な機能があるため、ソースコードを変更した際にわざわざ自分でコンパイルする必要がありません。
これによりレイアウト確認や動作チェックがしやすくなり、開発が捗るのがReactの開発環境周りの素晴らしい点です。
5.ReactとHTML+JSの比較
Reactは先程も言った通り、複雑なUI(アプリケーション)を開発する際に非常に重宝します。
一度使い始めると「二度と生JSでアプリ開発はしない」と思うくらいには楽になります。
これは、Reactというライブラリが、JavaScriptで画面更新する際の複雑な処理の数々を代わりに行ってくれるからです。
HTML+JSの辛い所
■ 処理の流れが追いづらい
JSからHTML要素を操作する必要がある場合を考えてみましょう。
まずdocument.getElementBy~
でDOM要素を取得し、その要素のプロパティを書き換えたり、createElement
で動的に要素を追加するのですが、大規模になればなるほどJavaScriptで選択したHTML要素が見つけづらくなり処理が追いづらくなります。
また、動的なアプリになるとHTMLで静的に定義した要素とJSで動的に定義した要素の関係性が複雑になり、一見何が起こるか分からないコードが生まれがちです。
■ バグが起きやすい
JavaScriptは、画面を制御するためにHTML要素から「セレクタ」でDOM要素を取得する必要があります。
当然、正しくセレクタを記述しないと動きません。
例:
<p id="counter">counter</p>
この要素を取得して、値を書き換える場合を考えてみましょう。以下のタイプミスは全てエラーになります。
//例1:counterではなくcount[o]rとなっている
document.getElementById("countor").innerText = count;
// ->当然動かない
//例2:セレクタが違う(IdではなくNameで取得しようとしている)
document.getElementsByName("counter")[0].innerText = count;
// ->name属性に「counter」は設定されていないので動かない
//例3:書き込むプロパティが違う(innerTextでなくvalueに書き込んでいる)
document.getElementById("counter").value = count
// ->pタグにvalue属性はないので動かない
// getElementBy~は、何のタグが返ってくるか分からないので属性の有効・無効が取るまで分からない
この他にも様々な記述ミスが存在すると思います。
これを回避するには、HTMLとJSを比較して目視チェックする必要があるのですが、大規模になればなるほど確認が困難になり、記述ミス起因のバグが起きやすくなります。
■ 記述量が多い
複雑なアプリを作ると特に目立ちますが、構文が長く冗長です。
JQueryを使えばある程度は解決できますが、やはりセレクタを介さないと画面の要素を操作できないのは面倒ですし、セレクタの管理も面倒です。
また、JavaScriptでHTML要素(Element)を作成するのはHTMLより非常にコストが高いです。
const h1 = document.createElement('h1');
h1.textContent = 'hello world!';
document.getElementById("root").appendChild(h1);
HTMLであれば、該当要素の中に<h1>hello world!</h1>
を入れるだけですが、JSでそれをやるには
- 親要素をセレクタで取得
- 追加したい要素を作成
- 親要素に追加
といった手順を踏む必要があり、HTMLと比べると構文が非常に長いです。
これも複雑なアプリになると実装コストが高すぎて辟易することになります。
このように、HTML+JSで複雑なアプリを作るのには莫大なコストがかかるため、ReactやVueの様なJSライブラリ/フレームワークが人気を博しているという訳です。
ReactがバニラJSより優れている点
1.構文の簡潔さ
JSX構文やReactの内部処理によって、同じような処理をReactの方が圧倒的に簡単に記述する事ができます。
2.保守のしやすさ
Reactは部品毎にHTMLとJavaScript(CSS)を分割し、それらを組み合わせてアプリを作るといった方式で開発するライブラリなので、HTML+JSよりもモジュール分割が容易です。
部品毎にHTMLとJavaScriptが管理できるので、1つのファイルで管理するより処理の流れが追いやすく、影響範囲も限定する事ができます。
つまり、保守が圧倒的にしやすいのです。
3.バグの発生頻度
HTMLとJavaScriptが分離している場合、セレクタでの取得をミスしてしまうとその後の処理が全て止まってしまいますが、HTMLとJavaScriptが合体しているJSXではそのような事が起こらないので、必然的にバグの発生頻度が減ります。また、TypeScriptを導入することで存在しないプロパティ等を静的解析してエラーを出す事ができるので、更に品質の高いコードを作成する事が可能です。
6.Reactは何故人気なのか?
Reactは数あるJSフレームワークの中でもトップに君臨しています。
他のJSフレームワークに比べて人気な理由は、私が考えるに以下の4点です。
1.拡張性・柔軟性が高いから
React自体はUI制御を簡単にするという最小限の機能のみ提供します。
それ以外の機能は、プロジェクトに応じてReact用の拡張ライブラリで外付けするといったスタイルです。
これにより、小規模~大規模アプリまで幅広く対応する事ができるのが特徴です。
React向けUIライブラリ「MUI」や「Chakra UI」を使えば、UIのデザインコストも削減可能です。
また、大規模向けには「Next.js」といったReactを用いたwebアプリを構築するためのフレームワークや、「Redux」といった状態管理ライブラリがあり、それらを使う事で大規模開発を効率的かつ安全に行う事が出来ます。
2.情報量が多いから
人気のライブラリなので、困ってもネットで検索すればすぐに対処法が出てくるのがReactを使う大きな利点です。コミュニティも大きいので、ネット上での質問も回答が返ってくる可能性が高いです。
3.JavaScriptで全部完結するから
JSXは慣れるまで正直違和感が凄いですが、JavaScriptでUIの実装が全て完結するのは、いざやってみると分かりますが非常に楽です。
JavaScriptが利用できるので、map関数を用いて配列データをHTMLのリストを簡単生成できたり、三項演算子を使えば、変数に応じて表示する部品を簡単に切り替える事ができます。
他のJavaScriptフレームワークである「Vue」や「Svelte」は独自の構文を使わないと、UI表示の条件分岐や繰り返しが使えないので、そういった点で素のJavaScriptに近いReactが最も好まれているのではないかと思います。
4.データフローが複雑化しにくいから
Reactは「単方向データフロー」を採用しています。
つまり「データは常に1方向に流れる」という事ですが、どういう事かというと、Reactは基本的に「状態(State)→UI(View)→イベント(Action)→状態(State)…」といった単方向にのみデータを流すことを許されており、UIの操作から状態を変更する際には必ずイベントを起こさないといけません。
一見面倒ですが、単方向にすることでデータフローが複雑化しにくい、テストしやすいといったメリットがあります。
双方向データバインディングは大規模になるとデータフローが入り乱れて非常にややこしくなるため、複雑化しにくい単方向データバインディングの方が好まれているのだと思います。
まとめ
今回はあえてReactそのものの機能紹介はせずに、概要や仕組みを何となく理解する事を目的とした記事を作ってみました。
というのも、私がReactを学習する際、初めからReactの使い方を叩き込まれたせいで、
Reactが一体本質的にどういったものなのか分からなかったのが全てのきっかけです。
そのせいで私は色々と間違った知識を持っておりました。
一番大きかったのが「ReactはNode.JSサーバーでしか動かない」といった誤解です。Reactの開発方法「だけ」を叩き込まれて内部的な仕組みや本番化方法を知らなかったのが原因ですね。
これからReactを学びたいと思っていらっしゃる皆様方には、
是非ともReactの仕組みを「何となく」理解してから望んでいただけると為になるのではないかと思います。