[React] よーし! いっちょReactやってみっか! #3 基本キーワード編

2020.10.05

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

CX事業本部の中安です。まいどです。

モバイルアプリエンジニアな自分がReactを始めてみることにした 「よーし! いっちょReactやってみっか!」シリーズの続きです。 今回もよろしくお願いします。

前回は作成されたプロジェクトの中身をサラッと見ていきました。

今回は、Reactでよく使われる基本キーワードを抑えていくことにします。 React独自なものやJavaScriptのものが混ざっていますが、 頭が混乱しないように言葉の定義を覚えておくのも大事ですね。

React

初回のブログでも「Reactとは」については書きましたが、あらためてこちらでもまとめようと思います。

React

ReactはFacebook社によって開発されたJavaScriptのためのユーザインターフェイス構築用のライブラリで、 VueAngularなどのWEBフロント用ライブラリに並ぶ人気のライブラリです。

他のライブラリとの比較はこちらの記事が参考になるでしょう。

Reactはプロジェクト化せずとも1つのHTMLファイルだけに取り入れることも可能ですし、他のライブラリとの共存も可能とされています。 プロジェクト化したとしても書き方に制約がかかることが少ないという意味では 「フレームワーク」ではなく「ライブラリ」という位置づけのほうが正しそうです。

以下はReact公式が掲げるReactの特長です。

宣言的ビュー

表示の構造をプログラマブルに書くことができ、 データ表示の配置場所を定義しておくことで、データが変更されたら同時に表示も変更されるという動きをすることを指します。

「表示の構造をプログラマブルに書く」とは、Reactの場合はHTMLライクな書き方「JSX」のことをいいます。

JSXについては後述します。

コンポーネントベース

複雑なUIであっても、それらを「部品 = コンポーネント」に分けることで煩雑なソースコードにはならないことを指します。 Reactは変更したい部分だけを修正できる特長があり、ソースコードの管理や再利用がしやすいとされています。

コンポーネントについては後述します。

Learn Once, Write Anywhere

「一度学習すれば、どこでも使える」という意味ですが、これはReactがWEBフロントサイドの技術だけではなく、 モバイルアプリやサーバサイドレンダリングなどでも利用することができることを指しています。

React.js

Reactは、そもそもJavaScriptでUIを構築するライブラリとして生まれた経緯からReact.jsとも呼ばれます。

しかしながら "Learn Once, Write Anywhere" の元、 モバイルアプリ開発用のReact派生であるReact Nativeであったり、 JavaScriptではなくTypeScriptを使用するReact+TypeScript構成であったり、 Reactという言葉自体が2010年代中盤からは狭義から広義な意味へと変わっています。

このブログもシリーズが続けば、React NativeReact+TypeScriptなども手を出していきたいところですが、 まずはReact.jsベースでやっていこうと思います。

DOMと仮想DOM

DOM

DOMDocument Object Modelの略で、 JavaScriptからHTMLXMLを操作するための「プログラミングインターフェイス」とされています。

WEBページであれば画面に表示されているHTMLの要素をひとつのモデルオブジェクトとして扱い、中の構造や内容を変更することが出来ます。

[まとめ]
DOM = JavaScriptからHTMLXMLを操作するための「プログラミングインターフェイス」

仮想DOM

DOMの操作は、ツリー構築から始まりレイアウト計算・描画というプロセスを経るらしく、 この処理をWEBブラウザが受け持っているために描画コストがかかってしまうとのことです。

Reactでは、これらをJavaScript側で受け持たせることで描画コストを下げる仕組みを使います。 この仕組みを「仮想DOM」(Virtual DOM)と呼びます。

仮想DOMを用いることで、必要最低限の差分を本来のDOMに反映するので、描画コストが向上するとのことです。

[まとめ]
仮想DOM = JavaScriptでDOM操作を処理して描画コストを下げる仕組み

ノードとエレメント

DOMを扱うにあたって「ノード」と「エレメント」という言葉がよく出てきます。 今一度整理してみましょう。

ノード

ざっくりいうと、DOMの中を構成するモノひとつひとつのことを総じてノードといいます。

エレメント

ざっくいうと、ノードの中でもHTML(またはXML)タグ自体のことをエレメントといいます。 日本語では「要素ノード」ともいいますが、要はノードの一部ということですね。

たとえば

<div id="root"><a href="http://hoge">リンク</a></div>

この場合、id=root<div>の中には、<a>というエレメントが1つ存在しています。 ノードはどれだけ存在しているかというと、<a>という要素ノードと、hrefという属性ノード、"リンク"というテキストノードが存在しています。

このあたり細かい話は以下の記事を参考ください。

JSXとBabel

JSX

ReactではJSXという仕組みをよく使うことになります。

JSXJavaScriptXMLのような構文記述ができるようになる言語拡張と説明されています。 簡単に言うとJSXHTMLを埋め込むような書き方ができるという仕組みです。 間違いたくないのは「HTMLを直接書いてるのではなく、あくまでHTMLライクに書ける」ということでしょうか。

let element = (
  <div>
    <h1>Hello World</h1>
  </div>
);

単純なJavaScriptであればHTMLを文字列で変数に代入するところですが、JSXはそういうことをしていないことが分かると思います。 また、値の全体をカッコで囲っていますが、これはあってもなくてもいいとのことです。 ここでは分かりやすくするためにカッコを付けています。

JSXについてはこちらのドキュメントも参考ください。

[まとめ]
JSX = HTMLライクな記述ができるJavaScriptの言語拡張

Babel

Babelは、前述のJSXJavaScriptのソースコード上で使用可能にするためのトランスコンパイラです。 これがないとJSXは動作せずエラーになってしまうことでしょう。

前ブログで作ったプロジェクト上のindex.jsでは、特にBaeblを意識をすることなくJSXを利用できていますが、 これは先にライブラリ群を呼び出しているからですね。

単独のHTMLの場合は、Babelをヘッダ等の<script>タグ内で呼び出した上で、 実装する箇所の<script>タグにはtype"text/babel"にしてやらないと正常動作しません。

<html>
<head>
  <!-- head部は色々と省略 -->
  <script src="(Babelの読み込み)"></script>
</head>
<body>
  <script type="text/babel">
    let element = (
      <div>
        <h1>Hello World</h1>
      </div>
    );
  </script>
</body>
</html>

[まとめ]
Babel = JSXを使用するために必要なコンパイラ

コンポーネント

Reactの特長は「コンポーネントベース」のフレームワークだということを先ほども書きました。 したがって「コンポーネント」という概念は大事になってきます。

「コンポーネント」は画面表示上の部品です。 Reactで作るWebアプリは、この部品を大きなものから小さなものまで組み合わせていきます。

部品一つ一つには状態やデータを持たせることができるので、 同じような表示箇所には、部品のデータだけを変えて何回も再利用することができる感じです。

たとえば、最初に作ったプロジェクトのテンプレートにあったApp.js内の「App」がそれにあたりますね。

関数コンポーネント

React上の「関数コンポーネント」とは、stateを持たずレンダリング処理だけを持つコンポーネントと定義されています。

名前の通り、クラスのような形式ではなく関数のように記述します。

function Parts(props) {
  return (
    <div>
      <h1>Hello World</h1>
    </div>
  );
}

また、関数コンポーネントはJavaScriptの「アロー関数」の構文を使って、もう少し簡略的に表すことができます。 上の関数コンポーネントは以下のようにも書くことができます。

const Parts = (props) => {
  return (
    <div>
      <h1>Hello World</h1>
    </div>
  );
}

stateについては後述します。

※関数コンポーネントはstateを持たないと冒頭に書きましたが、 Hooksという機能を使うことで同等のことが可能になります。これもまた後述します。

React.Component

関数コンポーネントとは違い、クラスでの記述となるコンポーネントも作ることもできます。 クラスであるゆえにプロパティやメソッド、コンストラクタなどが定義できます。

クラスがコンポーネントとして振る舞うためには React.Component を継承してやる必要があります。

そして、その継承したクラスはrender()というメソッドを実装する必要があり、 実際に表示するためのJSXなどを返してやらなければなりません。

class Parts(props) extends React.Component {
  
  constructor(props) {
    super(props);
  }
  
  render() {
    return (
      <div>
        <h1>Hello World</h1>
      </div>
    );
  }
}

関数コンポーネントはstateを持たないという性質でしたが、こちらは持つことができます。

props、state、プロパティ

ここからはコンポーネントの持たせる情報などの話になるのですが、 その前にReactでコンポーネントはどのように生成するのかを見なくてはいけません。

先程のJSXではHTMLライクにUIを宣言できると書きました。 「HTML」ではなく「HTMLライク」や「HTMLのように」と書いている理由は、 ここにコンポーネントを直接配置することができるからです。

たとえば、こんな感じです。

return (
  <div>
    <MenuItem />
    <MenuItem />
    <MenuItem />
  </div>
);

ここでいうMenuItemは、仮に自分で作ったコンポーネントです。 このようにHTMLのようにJSXを使ってコンポーネントの配置場所を定義できるわけですね。

しかし、これが何かしらのメニューのUIであるとして、今3つ並べたMenuItemコンポーネントはまったく同じUIとして表示されてしまうことでしょう。 それぞれのMenuItemはそれぞれに異なった情報を渡してやり、それぞれに異なった表示をさせる必要があります。

では、どうするのかというと、ここにHTMLXMLに与えるように属性値を与えてやればいいのです。

return (
  <div>
    <MenuItem title="アカウント管理" />
    <MenuItem title="商品管理" />
    <MenuItem title="顧客管理" />
  </div>
);

こうすると、それぞれがメニューのアイテムになっていて、なおかつタイトルが別々のものになるということが分かります。

これを踏まえて、コンポーネント側でこの情報をどう扱うかをキーワード別に整理していきます。

props

上記の例ではtitleが呼び出し元によって渡されました。 これをコンポーネント側で受け取って反映させる必要がありますが、 それらの渡された値(属性値)は、propsという値に入ってきます。

propsJavaScriptのオブジェクトになっていて、先程の例では {name: "アカウント管理"}といった感じで渡されてきます。

propsは関数コンポーネントであれば関数の引数で、クラスであればコンストラクタ、またはthis.propsで引き取ることができます。 また、このpropsは原則として読み取り専用のデータです。

[まとめ]
props = コンポーネントに渡された属性値をまとめてオブジェクト化されたもの

state

WEBアプリでは何かしらのユーザ操作やその他イベントによって、UIの一部を変更する必要があることが多いと思います。 例えば「ボタンを押したら、○○という文字列が△△に変わる」といった感じです。

言い換えると『コンポーネントの「状態○○」を「状態△△」に更新する』という状態管理をしていると言えます。

この状態管理で使う値をstateといいます。 状態の更新がUIにすぐに反映されるのが特長のReactでは大事な要素の一つですね。

クラスコンポーネントでstateを扱う場合はthis.stateという値を使って扱います。

stateJavaScriptのオブジェクトになっていますが、 状態を更新する場合はこのオブジェクトを直接書き換えるのではなく setState()というメソッドを使用することになります。

これらのstateの挙動については別途ブログでまとめようと思います。

[まとめ]
state = コンポーネントの状態をまとめてオブジェクト化したもの。この値の操作でコンポーネントの状態を操作できる

プロパティ

頭の整理のためにプロパティについても言及しておきます。

JavaScriptという言語に限らず、クラスというものには「メソッド」と「プロパティ」が存在します。

ここまで見てきたpropsstateは、プログラムに落とし込むと少しプロパティに似たような感じに映ります。 しかし、propsstateはコンポーネント独自のものですが、プロパティはクラスであればコンポーネントに限らず持てるものです。

オブジェクトに値を保管しておく「オブジェクト変数」と呼ばれるもので、stateのような"操作可能なコンポーネントの状態"とは分けて考えるべきですね。

[まとめ]
プロパティ = クラスから生成したオブジェクトに持たせておく値。コンポーネントの状態とは関係がない

Hooks

「コンポーネント」の項で「関数コンポーネントはstateを持たず、クラスコンポーネントはstateを持つ」と書きました。

しかし、2018〜2019年頃にReactに登場したHooksという機能が付けられたことで、その概念は変わったそうです。 クラスを使わずとも関数コンポーネントにてstateを使うことができるようになったからです。

状態管理を関数コンポーネントでも行えることになったことで、 クラスベースだったコンポーネント作成は関数ベースで作られることが増えたそうです。

その理由としては、クラスであるとコンポーネントの機能であるメソッドを書く場所が実装者によってバラバラに異なることがあります。 関数であるとその辛みが軽減されます。なぜなら、決まった機能は決まった箇所に書かれるからです。

実際にシンプルな例を書くと

const Parts = (props) => {
  const [name, setName] = useState("");
  return (
    <div>
      <h1>Hello {name}</h1>
    </div>
  );
}

ここにnameという状態(ステート)を定義しました。 ざっくりいうと、状態のGetterやSetter、そして初期値を設定しているようなイメージです。 なにやらクラスのようにも見えますが、これはあくまで関数ベースです。

Hooksはこのような状態管理の機能を含め、色々な他の機能も有しています。 ここでは書ききれないので、また触ってみて動きを確かめたら別途ブログにしたいと思います。

Hooksの扱い

さて、Reactの公式はこの後発機能であるHooksをどのようにしようと考えているかを確認してみます。

クラス型コンポーネントを削除する予定はない

ドキュメントに

クラスコンポーネントのユースケースをすべてフックがカバーできるようにする予定ではいますが、クラスコンポーネントのサポートも予見可能な将来にわたって続けていきます。Facebook では何万というコンポーネントがクラスとして書かれており、それらを書き換える予定は全くありません。代わりに、クラスと併用しながら新しいコードでフックを使っていく予定でいます。

と言及されているようにクラスベースのコンポーネントが使えなくなるということはないようですし、 もし古くからあるReactのプロジェクト内のクラスベースのコンポーネントを関数コンポーネント+Hooksに差し替えなくてはいけないということもなさそうです。

クラスコンポーネントを全部書き換える必要があるのですか?

いいえ。React からクラスを削除する予定はありません — 我々はみなプロダクトを世に出し続ける必要があり、クラスを書き換えている余裕はありません。新しいコードでフックを試すことをお勧めします。

採用するのはどちら?

同じようにドキュメントには

フック、クラスのいずれを使うべきですか、あるいはその両方でしょうか?

準備ができしだい、新しいコンポーネントでフックを試すことをお勧めします。チームの全員の準備が完了し、このドキュメントに馴染んでいることを確かめましょう。(例えばバグを直すなどの理由で)何にせよ書き換える予定の場合を除いては、既存のクラスをフックに書き換えることはお勧めしません。

クラスコンポーネントの定義内でフックを使うことはできませんが、クラス型コンポーネントとフックを使った関数コンポーネントとを 1 つのコンポーネントツリー内で混在させることは全く問題ありません。あるコンポーネントがクラスで書かれているかフックを用いた関数で書かれているかというのは、そのコンポーネントの実装の詳細です。長期的には、フックが React のコンポーネントを書く際の第一選択となることを期待しています。

これを見る限りはHooks。つまり関数コンポーネントを推していきたいという考えのようです。

新たにReactを始めるよという場合にはHooksを使う前提で良いかもしれません。 もちろん、クラスを使う必要性が出てくればそれを採用することもアリだと、ドキュメントで謳われていますね。

[まとめ]
Hooks = 関数コンポーネントでシンプルかつリッチな機能を実現させるためのReactの機構

ContextとProvider

Context

ReactにおけるContextは、複雑になるコンポーネント間のデータ共有を解決するための仕組みです。 コンポーネントは細分化されて配置していくと親子関係になっていくと思いますが、 親コンポーネントにあるデータを子コンポーネントにpropsで渡して、さらにその子コンポーネントにpropsでデータを渡して・・・とやっていくと 非常に煩雑になることでしょう。

Contextはそういったデータのバケツリレーを避けるために、共通で(もしくは特定のコンポーネント群で)データをプールさせます。

const MenuContext = React.createContext(data);

class MenuContainer extends React.Component {
  static contextType = MenuContext
}

class MenuItem extends React.Component {
  static contextType = MenuContext
}

このように外でReact.createContext()を使って共通データ用のContextを作成し、 各コンポーネント内でcontextTypeにセットしてやることで、 MenuContainerでもMenuItemでもContextの値が使用できるようになります。

使用する際にはthis.contextから値を引き取ることができるという仕組みです。

props以外で外からデータを渡せるという意味では覚えておきたいキーワードですね。

[まとめ]
Context = 複数のコンポーネント間で煩雑なデータのやりとりをしないように用意する共有データ。props以外から外部からデータを取れる方法でもある

Provider

Contextを使うと、複数のコンポーネント間でひとつのデータを取り扱えるということでした。

しかし、そうなってくると同一コンポーネントであれば、常に同じContextのデータを使用することになってしまい、 別の辛みが発生してしまいます。

そこでProviderという仕組みを使います。Contextで渡される値を任意のコンポーネントオブジェクトだけ違う値を渡せるという機構です。

const MenuContext = React.createContext(data);

class MenuItem extends React.Component {
  static contextType = MenuContext
}

// レンダリングの部分だけ抽出
return (
  <div>
    <MenuItem />
    <MenuItem />
    <MenuContext.Provider value={anotherData}>
      <MenuItem />
    </MenuContext.Provider>
  </div>
);

この例でいうとMenuItemコンポーネントが3つに並んでるのですが、 最後のひとつだけMenuContext.Providerというタグに囲まれています。

こうすることで、3つ目のMenuItemだけ dataではなくanotherDatacontextから取得できるようになります。

この仕組みはテーマの分岐などでよく使われるとのことです。

[まとめ]
Provider = Contextのデータを一部だけ違うものに差し替えるための機構

Redux

Reactのコンポーネントではそれぞれ個別に状態を管理しています。 それらを共通化できる仕組みとして前項ではContextについて書きましたが、 さらに複雑になってくるとそれでも追いつけなくなると思います。

そこで登場するのがReduxになります。

Reduxは状態を統合して管理するユーティリティツールにあたります。 他にも同様のツールはあるそうですが、こちらがデファクトスタンダードのようですね。 Reactとは別モノなので別途インストールが必要になるとのことです。

現時点ではまだReduxは触れていないので、こちらもまた色々と検証してみてから別途ブログにまとめようと思います。

[まとめ]
Redux = アプリの値(状態)や処理を統合して管理できるReactからは独立したユーティリティツール

まとめのまとめ

React

  • React: ReactはFacebook社によって開発されたJavaScriptのためのユーザインターフェイス構築用のライブラリ
    • 宣言的ビュー
    • コンポーネントベース
    • Learn Once, Write Anywhere

DOM

  • DOM: JavaScriptからHTMLXMLを操作するための「プログラミングインターフェイス」
  • 仮想DOM: JavaScriptでDOM操作を処理して描画コストを下げる仕組み
  • ノード: DOMの中を構成するモノひとつひとつのこと
  • エレメント: ノードの中でもHTML(またはXML)タグ自体のこと。「要素ノード」

JSX

  • JSX: HTMLライクな記述ができるJavaScriptの言語拡張
  • Babel: JSXを使用するために必要なコンパイラ

コンポーネント

  • コンポーネント: 画面表示上の部品
    • 関数コンポーネント: stateを持たずレンダリング処理だけを持つ関数構文で作られたコンポーネント。
    • クラス型コンポーネント: React.Componentを継承したクラス記述でのコンポーネント

コンポーネント内の値

  • props: コンポーネントに渡された属性値をまとめてオブジェクト化されたもの
  • state: コンポーネントの状態をまとめてオブジェクト化したもの。この値の操作でコンポーネントの状態を操作できる
  • プロパティ: クラスから生成したオブジェクトに持たせておく値。コンポーネントの状態とは関係がない

フック

  • Hooks: 関数コンポーネントでシンプルかつリッチな機能を実現させるためのReactの機構

コンテキスト

  • Context: 複数のコンポーネント間で煩雑なデータのやりとりをしないように用意する共有データ。props以外から外部からデータを取れる方法でもある
  • Provider: Contextのデータを一部だけ違うものに差し替えるための機構

Redux

  • Redux: アプリの値(状態)や処理を統合して管理できるReactからは独立したユーティリティツール

最後に

ついつい長くなってしまいましたが、Reactでの開発で使いそうなキーワードをまとめてみました。 他にも必要なキーワードはまだまだありそう(例えばデザイン系とか)ですが、今回はこれくらいにしておこうと思います。

次回からは実際にReactのプログラムを書いていくようなブログにしたいなぁと思っています。

では、またー。