Reactに対する解像度が高まるとてもいい記事でした。公式ドキュメントを元に解説されており、ボヤケていた輪郭が見えてくるような内容です。React の入門を一通り終えた後に読むと理解が深まり丁度いいのではないかと思いました。
また、冒頭に
React について少し深く知ることで、さらに React を好きになったという方を一人でも多く増やしたい。その思いから本記事を執筆しました。
と記載があります。確かに、読んだ後はReact(プログラミング言語)への苦手意識が薄れ、少し好きになりました。この記事はアウトプットが目的ですが、もっと早く出会いたかったという思いもあり、1人でも多くの人に知ってほしい所存です。
ここでは、記事の要約とReactへの見方がどう変わったかを書いていきます。
ReactはUIを構築するためのJSライブラリ
聞いたことはあると思います。ただ、これだけ読んでも「何か部品のコンポーネントを作って組み合わせてwebアプリを構築していくということかな?」ぐらいの認識でした。よくわからないので掘り下げてみましょう。
これに対する解説の前に、まずUIとは何かを確認します。
結論、UI とは 「人間と機械の境界であり、双方向的なやりとりが生まれるところ」 です。
どういうことか?
参考元だと「スリープ状態のスマートフォン」を例に説明されています。
スマホは、指紋登録した指をセンサーに当てると、スリープ状態が解除されてディスプレイは明るくなり画面ロックが解除されます。この指をセンサーに押し当てる行為は人からスマホ(機械)へのアクションです。
対して、画面ロックが解除されたことは 指紋認証に成功したことを知らせるスマホから人に対するアクションです。 (失敗してバイブレーションが作動することもスマホから人)
他にも、エレベータのボタンを押すことは人から機械へのアクションであり、行き先が光ることや「ドアが開きます」等のアナウンスが機械から人へのアクションになります。
フォームへ入力したとして、入力そのものや送信ボタンを押すことが人から機械であり、エラー表記「必須項目です」を出したり、正しい場合の挙動(ページ遷移など)といった反応を表すことが機械から人になります。
この 双方向的なやりとりがうまれるところ がUIです。
つまり、Reactはアプリケーションの状態を利用者に表現し(機械から人)、利用者がアプリケーションを操作するための(人から機械)インターフェースを提供する JavaScript のライブラリだということです。
これが「ReactはUIを構築するためのJSライブラリ」になります。
UI とはアプリケーションの public な部分である
もう少しUIについて掘り下げると、UIは「人と機械との双方向的なやりとりがうまれるところ」であり、「アプリケーションの public な部分」なのです。
例えば、あるオブジェクトの private
メソッドはオブジェクトの利用側から呼び出すことができません。オブジェクトを利用する側が呼び出せるのは public
メソッドのみです。
この public / private はまとめて visibility
と呼ばれます。
スキルみたいで覚えやすいですね。
オブジェクトの利用側からは、オブジェクトの public
メソッドしか見えないのです。 private
メソッドは見えず、見えないものは使えません。
ザックリですが、つまりUIとは「アプリケーションのpublicな部分」とも言えます。
カプセル化
余談ですが、 private
という visibility
は、メソッドやプロパティがクラスというカプセルに閉じ込められているのでオブジェクトの利用者側からは見えません。この中身を知らなくてもいい概念を「カプセル化」または「ブラックボックス化」といいます。カプセル化された仕組みは詳しく知らなくてもいいのです。
例えば、銀行口座を表すクラスを作るとして、内部実装の隠蔽のため、クラスの内部データや実装の詳細を外部から直接見えないようにします。また、アクセス制御が目的で、 private
や protected
などのアクセス修飾子を使用して、クラス外部からのアクセスを制限します。こうしてカプセル化することで、口座残高の整合性を保ち、不正な操作を防ぐことができます。
外部とのやり取りは、公開されたメソッド(publicメソッド)を通じてのみ行うのです(インターフェースの提供)
以上、「ReactはUIを構築するためのJSライブラリ」でした。
宣言的(Declarative)
続いて、React の公式ドキュメントに記述されている「Declarative」(宣言的)という単語について掘り下げていきます。
以下、公式ドキュメントの日本語訳です。
宣言的な View
React は、インタラクティブなユーザインターフェイスの作成にともなう苦痛を取り除きます。アプリケーションの各状態に対応するシンプルな View を設計するだけで、React はデータの変更を検知し、関連するコンポーネントだけを効率的に更新、描画します。宣言的な View を用いてアプリケーションを構築することで、コードはより見通しが立ちやすく、デバッグのしやすいものになります。
ここでは、この文章をメインに掘り下げていきます。
ReactはインタラクティブなUIを構築する
公式の文章「React は インタラクティブ な UI の作成にともなう苦痛を取り除く」について、
React makes it painless to create interactive UIs.
インタラクティブとは「双方向」を意味しており、先程のUIの説明で双方向なのは明白です。ここでは敢えて 「Vanilla JS や jQueryといった既存技術で interactive な UI を作るのは大変だと暗示しているのだろう」と解説されていました。
この「宣言的」を理解するために、まずは「sateに応じてviewが変化する」という記述を要約します。
sateに応じてviewが変化する
Design simple views for each state in your application
「アプリケーションの状態ごとにシンプルなviewを設計する」と記載されており、アプリケーションの状態とviewは基本的に1対1であることが読み取れます。
state | view |
---|---|
count = 0 | □ |
count = 1 | △ |
count = 2 | ◯ |
と言われてもイメージしづらいので例を記載します。
以下、ボタンをクリックすると数字が増加し、その状態に応じて表示が変化します。
import React, { useState } from 'react';
function Counter() {
// count という state を定義し、初期値を 0 とする
const [count, setCount] = useState(0);
// カウントを増やす関数
const incrementCount = () => {
setCount(count + 1);
};
// state の値に応じてメッセージを変える
const getMessage = () => {
if (count === 0) return "カウント開始";
if (count < 5) return "5回";
if (count < 10) return "10回";
return "10回以上クリックしてます!";
};
return (
<div>
<h1>カウンター: {count}</h1>
<p>{getMessage()}</p>
<button onClick={incrementCount}>+</button>
</div>
);
}
export default Counter;
buttonをクリックする度に増えるCounterの数字が React の state(状態) です。
このstate が変化することで view(ここでは DOM) が変化します。
これが「state に対応するシンプルな view を設計する」です。
データの変更をトリガーにしてコンポーネントを更新する
React will efficiently update and render just the right components when your data changes.
「Reactはデータが変更されたときに、変更されたコンポーネントを効率的に更新して再描画する」と訳されます。
参考元では、この一文を「update and render」と「when your data changes」に分けて考ていました。再描画、公式ドキュメントを読んでもわからないんですよね...確かイメージしやすいようレストランに例えられていたのですが、やっぱりわからなかったです。
update and render
- Reactは 仮想DOM(Virtual DOM) をメモリ上に構築します。これは実際のDOMとは異なります。
- 仮想DOM は、Reactコンポーネント内の DOM(React Element) を実際に描画されているDOMに一致させる役割を担います
- React コンポーネントに状態の変化があれば、React が構築した 仮想DOM が差分を検知します(reconciliation)
1~3より、update は
・React コンポーネントから 仮想DOM に対して変更を通知すること
・render(描画) は Real DOM に対する変更の反映を指す
と解説されています。
つまり「update and render」とは、React コンポーネントの変更を検知し、その変化を 実際のDOM に反映することです。
when your data changes
React 内で変化する data
とは、 state
か props
のみです。
そのため、data
という言葉は React では特別な意味を持ちます。
state
は 例の記述 にあるようなコンポーネント内の状態です。コンポーネント自身が管理する内部データであり、親コンポーネントが持っている state
は、子コンポーネントから直接見ることができません。
一方、 props(property)
は「親から子に渡される、子内で変化しない値」です。また、子は親の state
を参照できず「読み取り専用」の値となります。
この props
または state
が変化すると、React はコンポーネントを再描画します。
名称 | 説明 |
---|---|
親コンポーネント | 情報を持っている人(state) |
子コンポーネント | 親からメッセージを受け取る人(props) |
props | 親から子へ渡されるメッセージ(変更不可) |
また、再描画に関しては以下のように記載があり
簡単に書くと、render とは「React コンポーネントという JS の関数(またはクラス)の初回実行」であり、rerender は「state もしくは props の変化をトリガーとした、関数の(またはクラスコンポーネントの render メソッドの)再実行」です。
render: 初回実行(お店のオープン)
rerender: 再実行(商品の入れ替えや再配置)
という解釈をしました。
宣言的なインフラ
宣言的という考え方はフロントエンド独自のものではありません。Infrastructure as Code の文脈でも宣言的なアプローチが主流になっています。例えば、「コンテナを4つ起動する」という命令を記述するとこの命令を2度実行すればコンテナが8つ立ち上がります。しかし、「コンテナは4つである」と望ましい状態(desired)を宣言しておくとコンテナの数が0個のときは4つ立ち上げます。何らかの理由でコンテナの数が3つになったときは、合計4つになるように新しいコンテナを1つ立ち上げます。React の Declarative View も、もちろん後者と同じことを指します。予測可能であることと宣言的であることは密接に関連しているのです。
宣言的であることのメリットは、コードが読みやすくなりコードの動作を予測しやすくなることです。これはデバックを簡単にすることにも繋がります。
この辺りはひとまず頭にいれておき、実際に書くときや他の方が書かれたコードを読む際に意識しておきたいです。
コンポーネントに基づいている(Component-Based)
「Component-Based」です。参考元からそのまま持ってきます。
コンポーネントベース
自分自身の状態を管理するカプセル化されたコンポーネントをまず作成し、これらを組み合わせることで複雑なユーザインターフェイスを構築します。コンポーネントのロジックは、Template ではなく JavaScript そのもので書くことができるので、様々なデータをアプリケーション内で簡単に取り回すことができ、かつ DOM に状態を持たせないようにすることができます。
自身の状態を管理するカプセル化されたコンポーネントを作る
Build encapsulated components that manage their own state
stateに応じてviewが変化するでは state
に対応した view を用意するということが主眼でしたが、この文は コンポーネントが自身の状態を管理することに焦点が当てられています。
これはとても重要で、コンポーネントが自身の状態(ローカルステート)を管理していれば、挙動を変更するとき、そのコンポーネントを探し出して書き換えるところから作業をスタートできます。
呼び出し側はコンポーネントの内部実装を知らなくても良い
また、コンポーネントはそれぞれカプセル化されているため、コンポーネントの内部実装を知らなくても呼び出して利用できます。
コンポーネントを呼び出す側が知っていなければならないのは、
・コンポーネントの名前
・ファイルパス
・props として渡す値
です。
一度学べばどこでも書ける(Learn Once, Write Anywhere)
React と組み合わせて使用する技術に制限はありません。React を使って新しい機能を追加する際に、既存のソースコードを書き換える必要はありません。React は Node を使ったサーバ上でもレンダーできますし、React Native を使うことでモバイルアプリケーションの中でも動きます。
React 以外の技術スタックは仮定していないので、既存コードを書き変えずに新機能を開発できる
We don’t make assumptions about the rest of your technology stack,
React は JavaScript で記述されているため、npm で管理されているあらゆる JS パッケージを利用できます。つまり JS エコシステムの資産をフル活用できます。
また、「新機能の開発で既存コードを書き換えずに済む」というのは Progressive(漸進的)に React を導入できるということだと考えられております。
例えばHTMLの中に新しく <div>
を用意してReact記述のコンポーネントをマウントすれば、一部分だけReactで実装したことになります。
React はサーバーでもモバイルアプリでも描画できる
上記はReact を深く知るために最も重要な文章です。
実はReactとは 「ツリー構造の Virtual DOM(仮想 DOM) を構築して差分検知をするライブラリ」 なのです。
簡単にまとめると Virtual DOM とは「実際のツリー構造に対応するメモリ内に構築した仮想のツリー構造そのもの」と「実際のツリー構造と仮想のツリー構造との差分検知アルゴリズム」
と説明されています。
つまり、以下のようにまとめられます。
名称 | 説明 |
---|---|
React | Virtual DOM を構築するライブラリ |
Virtual DOM | 実際のツリー構造との差分検知に使われるもの |
ではなぜ、React を使えば Web アプリケーションを作れたり、React Native を使えばモバイルアプリが制作できるのか?
それは、 React が検知した差分を通知する相手が異なるからです。
Web アプリケーションでは react-dom
という Renderer を使います。React は Virtual DOM の差分を検知すると react-dom
に通知します。 react-dom
の内部には JS の DOM 操作 API が実装されているので、通知に基づいて実際の DOM を書き換えます。
モバイルアプリであれば、 react-native
という Renderer に検知した差分を通知します。
名称 | 通知先 |
---|---|
Webアプリ | react-dom |
モバイルアプリ | react-native |
まとめると、
・React は Virtual DOM を構築し、差分検知をするライブラリ
・Renderer に応じて UI を表示する対象を自由に変えられる
要約は以上です。
感想
冒頭にも書きましたが、読む前に比べてreactへの解像度が少し高くなりました。
元々ほとんど見えていない状態で、誰かが作成してくれたハンズオンの構成・記述しか正解がありませんでしたが、今では選択肢ぐらいはあるかなと思います。
また、著者の思いが伝わってくるような文面だったこともあり、より意欲的に取り組めた印象です。
素敵な記事をありがとうございました!
これからも定期的に復習し、公式ドキュメント等を読むことで記事では語っていないことも学んでいこうと思いました。